import React, { useEffect, useMemo, useState } from 'react'; import { View, StyleSheet, TouchableOpacity, Modal, KeyboardAvoidingView, Platform, FlatList, ListRenderItem, Alert, } from 'react-native'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { colors, spacing, fontSizes, borderRadius } from '../../../theme'; import { authService } from '../../../services/authService'; import { useAuthStore } from '../../../stores'; import { Avatar, EmptyState, Loading, Text } from '../../../components/common'; import { User } from '../../../types'; type MutualFollowSelectorModalProps = { visible: boolean; title?: string; confirmText?: string; confirmingText?: string; confirmLoading?: boolean; excludeUserIds?: string[]; initialSelectedIds?: string[]; onClose: () => void; onConfirm: (selectedUsers: User[]) => void | Promise; }; const MutualFollowSelectorModal: React.FC = ({ visible, title = '邀请成员', confirmText = '确认', confirmingText = '处理中...', confirmLoading = false, excludeUserIds = [], initialSelectedIds = [], onClose, onConfirm, }) => { const { currentUser } = useAuthStore(); const [friendList, setFriendList] = useState([]); const [friendLoading, setFriendLoading] = useState(false); const [selectedIds, setSelectedIds] = useState>(new Set()); const currentUserId = currentUser?.id; const excludeIdsKey = useMemo(() => excludeUserIds.join(','), [excludeUserIds]); const initialSelectedIdsKey = useMemo(() => initialSelectedIds.join(','), [initialSelectedIds]); const stableExcludeUserIds = useMemo(() => excludeUserIds, [excludeIdsKey]); const stableInitialSelectedIds = useMemo(() => initialSelectedIds, [initialSelectedIdsKey]); useEffect(() => { if (!visible) return; if (!currentUserId) return; const nextSelectedIds = new Set(stableInitialSelectedIds); setSelectedIds(prev => { if (prev.size !== nextSelectedIds.size) return nextSelectedIds; for (const id of prev) { if (!nextSelectedIds.has(id)) return nextSelectedIds; } return prev; }); let cancelled = false; const loadMutualFollows = async () => { setFriendLoading(true); try { const excludedIdSet = new Set(stableExcludeUserIds); const [following, followers] = await Promise.all([ authService.getFollowingList(currentUserId, 1, 100), authService.getFollowersList(currentUserId, 1, 100), ]); if (cancelled) return; const followerIdSet = new Set(followers.map(user => user.id)); const mutualFriends = following.filter(user => followerIdSet.has(user.id)); const availableFriends = mutualFriends.filter(user => !excludedIdSet.has(user.id)); setFriendList(availableFriends); } catch (error) { console.error('获取互关好友失败:', error); Alert.alert('错误', '获取互关好友失败'); } finally { if (!cancelled) { setFriendLoading(false); } } }; loadMutualFollows(); return () => { cancelled = true; }; }, [visible, currentUserId, stableExcludeUserIds, stableInitialSelectedIds]); const toggleSelection = (user: User) => { const next = new Set(selectedIds); if (next.has(user.id)) { next.delete(user.id); } else { next.add(user.id); } setSelectedIds(next); }; const handleConfirm = () => { const selectedUsers = friendList.filter(user => selectedIds.has(user.id)); onConfirm(selectedUsers); }; const renderFriendItem: ListRenderItem = ({ item }) => { const isSelected = selectedIds.has(item.id); return ( toggleSelection(item)} activeOpacity={0.7} > {item.nickname} @{item.username} {isSelected && ( )} ); }; return ( 取消 {title} {confirmLoading ? confirmingText : confirmText} 仅显示互相关注用户 {selectedIds.size > 0 && ( 已选择 {selectedIds.size} 人 )} {friendLoading ? ( ) : ( item.id} contentContainerStyle={styles.friendListContent} showsVerticalScrollIndicator={false} ListEmptyComponent={( )} /> )} ); }; const styles = StyleSheet.create({ modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'flex-end', }, modalContent: { backgroundColor: colors.background.paper, borderTopLeftRadius: borderRadius['2xl'], borderTopRightRadius: borderRadius['2xl'], height: '80%', paddingHorizontal: spacing.lg, paddingTop: spacing.lg, paddingBottom: spacing.xl, }, modalHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: spacing.lg, }, modalHeaderButton: { paddingVertical: spacing.sm, minWidth: 60, }, modalTitle: { fontWeight: '700', fontSize: fontSizes.xl, }, confirmButton: { fontWeight: '600', textAlign: 'right', }, tipContainer: { flexDirection: 'row', backgroundColor: colors.background.default, borderRadius: borderRadius.lg, padding: spacing.xs, marginBottom: spacing.md, }, tipText: { fontWeight: '600', }, selectedBadge: { backgroundColor: colors.primary.light + '20', paddingHorizontal: spacing.md, paddingVertical: spacing.xs, borderRadius: borderRadius.sm, alignSelf: 'flex-start', marginBottom: spacing.md, }, friendListContent: { paddingBottom: spacing.xl, }, friendItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: spacing.md, paddingHorizontal: spacing.sm, borderRadius: borderRadius.lg, marginBottom: spacing.xs, }, friendItemSelected: { backgroundColor: colors.primary.light + '15', }, friendInfo: { flex: 1, marginLeft: spacing.md, marginRight: spacing.sm, }, nickname: { fontWeight: '500', marginBottom: 2, }, checkbox: { width: 26, height: 26, borderRadius: 13, borderWidth: 2, borderColor: colors.divider, justifyContent: 'center', alignItems: 'center', }, checkboxSelected: { backgroundColor: colors.primary.main, borderColor: colors.primary.main, }, }); export default MutualFollowSelectorModal;