import React, { useEffect, useMemo, useState } from 'react'; import { View, StyleSheet, ActivityIndicator, Alert, ScrollView } from 'react-native'; import { useNavigation, useRoute, RouteProp } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { SafeAreaView } from 'react-native-safe-area-context'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { colors, spacing, borderRadius, shadows } from '../../theme'; import { Avatar, Text } from '../../components/common'; import { RootStackParamList } from '../../navigation/types'; import { groupService } from '../../services/groupService'; import { groupManager } from '../../stores/groupManager'; import { GroupMemberResponse } from '../../types/dto'; import { GroupInfoSummaryCard, DecisionFooter } from './components/GroupRequestShared'; type Route = RouteProp; type Navigation = NativeStackNavigationProp; const GroupInviteDetailScreen: React.FC = () => { const route = useRoute(); const navigation = useNavigation(); const { message } = route.params; const { extra_data } = message; const [loadingMembers, setLoadingMembers] = useState(false); const [members, setMembers] = useState([]); const [submitting, setSubmitting] = useState(false); const [memberCount, setMemberCount] = useState(null); const [loadingGroup, setLoadingGroup] = useState(false); const canAction = extra_data?.request_status === 'pending'; useEffect(() => { const loadGroup = async () => { if (!extra_data?.group_id) return; setLoadingGroup(true); try { const group = await groupManager.getGroup(extra_data.group_id); setMemberCount(group.member_count ?? null); } catch { setMemberCount(null); } finally { setLoadingGroup(false); } }; loadGroup(); }, [extra_data?.group_id]); useEffect(() => { const loadMembers = async () => { if (!extra_data?.group_id) return; setLoadingMembers(true); try { const res = await groupManager.getMembers(extra_data.group_id, 1, 100); const admins = (res.list || []).filter(m => m.role === 'owner' || m.role === 'admin'); setMembers(admins); } catch { setMembers([]); } finally { setLoadingMembers(false); } }; loadMembers(); }, [extra_data?.group_id]); const handleDecision = async (approve: boolean) => { const flag = extra_data?.flag; const groupId = extra_data?.group_id; if (!flag) { Alert.alert('提示', '缺少邀请标识,无法处理'); return; } if (!groupId) { Alert.alert('提示', '缺少群组标识,无法处理'); return; } setSubmitting(true); try { await groupService.respondInvite(groupId, { flag, approve }); Alert.alert('成功', approve ? '已同意加入群聊' : '已拒绝邀请', [ { text: '确定', onPress: () => navigation.goBack() }, ]); } catch { Alert.alert('操作失败', '请稍后重试'); } finally { setSubmitting(false); } }; const groupNo = useMemo(() => extra_data?.group_id || '-', [extra_data?.group_id]); const formatGroupNo = (id: string) => { if (id.length <= 12) return id; return `${id.slice(0, 6)}...${id.slice(-4)}`; }; return ( 群主与管理员 {members.length}人 {loadingMembers ? ( ) : ( {members.slice(0, 12).map((member, index) => ( {member.role === 'owner' && ( )} ))} {members.length === 0 && ( 暂无可展示的管理员信息 )} )} handleDecision(false)} onApprove={() => handleDecision(true)} processedText="该邀请已处理" /> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.background.default, }, scrollView: { flex: 1, }, scrollContent: { padding: spacing.lg, paddingBottom: spacing.xl, }, card: { backgroundColor: colors.background.paper, borderRadius: borderRadius.lg, padding: spacing.lg, ...shadows.sm, }, cardHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: spacing.md, }, cardIconContainer: { width: 30, height: 30, borderRadius: 15, backgroundColor: colors.info.light + '30', alignItems: 'center', justifyContent: 'center', marginRight: spacing.sm, }, cardTitle: { fontWeight: '600', flex: 1, }, memberCount: { marginRight: spacing.xs, }, loadingWrap: { paddingVertical: spacing.md, alignItems: 'center', }, memberPreview: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', }, memberAvatar: { marginLeft: -8, marginBottom: spacing.sm, }, memberAvatarFirst: { marginLeft: 0, }, ownerBadge: { position: 'absolute', bottom: -2, right: -2, backgroundColor: colors.warning.main, borderRadius: 8, paddingHorizontal: 4, paddingVertical: 1, }, ownerBadgeText: { fontSize: 10, fontWeight: '700', }, }); export default GroupInviteDetailScreen;