import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { GroupMemberRole } from 'tuikit-atomicx-react/chat'; import { Dialog, Toast, useUIKit, IconChevronLeft } from '@tencentcloud/uikit-base-component-react'; import cs from 'classnames'; import { View } from 'tuikit-atomicx-react/chat'; import { useUIContext, useGroupStore, useLoginStore } from 'tuikit-atomicx-react/chat'; import { GroupPermission, hasGroupPermission } from '../../../../utils/chatGroupPermission'; import { UserPicker } from 'tuikit-atomicx-react/chat'; import { SettingItem } from '../../SettingItem'; import { GroupMembers } from '../GroupMembers'; import classes from './GroupManagement.module.scss'; import type { IUserPickerRow, IUserPickerRef } from 'tuikit-atomicx-react/chat'; import type { GroupInfo, GroupMember } from 'tuikit-atomicx-react/chat'; interface GroupManagementProps { className?: string; onBack?: () => void; } const GroupManagement = forwardRef((props, ref) => { const { className, onBack } = props; const { t } = useUIKit(); const { activeConversationID, memberList, loadMembers, setMemberRole, muteMember } = useUIContext(); const { joinedGroupList, muteAllMembers, getGroupInfo } = useGroupStore(); const { loginUserInfo } = useLoginStore(); const currentGroupID = activeConversationID?.startsWith('GROUP') ? activeConversationID.slice(5) : undefined; const groupInfo = useMemo(() => joinedGroupList.find(g => g.groupID === currentGroupID), [joinedGroupList, currentGroupID]); const currentUserRole = groupInfo?.selfRole; const groupType = groupInfo?.groupType; const currentUserID = loginUserInfo?.userID; // Fetch full group info for isAllMuted const [fullGroupInfo, setFullGroupInfo] = useState(undefined); useEffect(() => { if (!currentGroupID) { setFullGroupInfo(undefined); return; } getGroupInfo(currentGroupID).then(setFullGroupInfo).catch(() => setFullGroupInfo(undefined)); }, [currentGroupID]); // Keep fullGroupInfo in sync with joinedGroupList updates useEffect(() => { if (!fullGroupInfo || !currentGroupID) { return; } const currentGroup = joinedGroupList.find(g => g.groupID === currentGroupID); if (currentGroup) { setFullGroupInfo(prev => ({ ...prev, ...currentGroup })); } }, [joinedGroupList, currentGroupID]); const isMuteAllMembers = fullGroupInfo?.isAllMuted; const allMembers = memberList; const adminMembers = useMemo(() => allMembers.filter(m => m.role === GroupMemberRole.Admin), [allMembers]); const [isShowUserPickerDialog, setIsShowUserPickerDialog] = useState(false); const [userPickerTitle, setUserPickerTitle] = useState(''); const [userPickerLockedItems, setUserPickerLockedItems] = useState>>([]); const userPickerRef = useRef(null); const memberActionRef = useRef<'promote_admin' | 'demote_admin' | 'mute' | 'unmute' | null>(null); const mutedMembers = useMemo(() => allMembers.filter((member: GroupMember) => { if (member.muteUntil === undefined) { return false; } return member.muteUntil > Date.now(); }), [allMembers]); const handleAdminManagement = useCallback((action: 'promote' | 'demote') => { if (!allMembers || allMembers.length === 0) { return; } if (action === 'promote') { memberActionRef.current = 'promote_admin'; setUserPickerTitle(t('ChatSetting.set_admin')); setUserPickerLockedItems([]); } else { memberActionRef.current = 'demote_admin'; setUserPickerTitle(t('ChatSetting.unset_admin')); setUserPickerLockedItems(currentUserRole === GroupMemberRole.Admin ? [{ key: currentUserID! }] : []); } setIsShowUserPickerDialog(true); }, [allMembers, currentUserID, currentUserRole, t]); const handleMuteManagement = useCallback((action: 'mute' | 'unmute') => { if (!allMembers || allMembers.length === 0) { return; } setIsShowUserPickerDialog(true); if (action === 'mute') { memberActionRef.current = 'mute'; setUserPickerTitle(t('ChatSetting.mute_members')); setUserPickerLockedItems( allMembers .filter((member: GroupMember) => { if (member.userID === currentUserID) { return true; } if (member.role === GroupMemberRole.Owner) { return true; } if (currentUserRole === GroupMemberRole.Admin && member.role === GroupMemberRole.Admin) { return true; } return false; }) .map((member: GroupMember) => ({ key: member.userID, label: member.nickname || member.userID, avatarUrl: member.avatarURL ?? '', })), ); } else { memberActionRef.current = 'unmute'; setUserPickerTitle(t('ChatSetting.unmute_members')); setUserPickerLockedItems([]); } }, [allMembers, currentUserID, currentUserRole, t]); const handleUserPickerConfirm = useCallback(async () => { const selectedItems = userPickerRef.current?.getSelectedItems(); if (!selectedItems || selectedItems.length === 0) { return; } const action = memberActionRef.current; if (!action) { return; } try { await Promise.all(selectedItems.map((item) => { switch (action) { case 'promote_admin': return setMemberRole(item.key, GroupMemberRole.Admin); case 'demote_admin': return setMemberRole(item.key, GroupMemberRole.Member); case 'mute': return muteMember(item.key, 86400 * 30); case 'unmute': return muteMember(item.key, 0); default: return Promise.resolve(); } })); Toast.success({ message: t('ChatSetting.operation_success'), }); setIsShowUserPickerDialog(false); await loadMembers(); } catch { Toast.error({ message: t('ChatSetting.operation_failed'), }); } }, [loadMembers, muteMember, setMemberRole, t]); const handleMuteAllToggle = useCallback((checked: boolean) => { if (!currentGroupID) { return; } muteAllMembers(currentGroupID, checked).then(() => { Toast.success({ message: checked ? t('ChatSetting.all_members_muted') : t('ChatSetting.all_members_unmuted'), }); }).catch(() => { Toast.error({ message: t('ChatSetting.mute_all_failed'), }); }); }, [currentGroupID, muteAllMembers, t]); const userPickerDataSource = useMemo(() => { if (!allMembers || !isShowUserPickerDialog) { return []; } const action = memberActionRef.current; let filteredMembers: GroupMember[] = allMembers; switch (action) { case 'promote_admin': filteredMembers = allMembers.filter((member: GroupMember) => member.role === GroupMemberRole.Member); break; case 'demote_admin': filteredMembers = adminMembers; break; case 'mute': filteredMembers = allMembers.filter((member: GroupMember) => !member.muteUntil || member.muteUntil <= Date.now()); break; case 'unmute': filteredMembers = allMembers.filter((member: GroupMember) => member.muteUntil !== undefined && member.muteUntil > Date.now()); break; default: break; } return filteredMembers.map((member: GroupMember) => ({ key: member.userID, label: `${member.nickname || member.userID} (${member.userID === currentUserID ? t('ChatSetting.me') : t(`ChatSetting.group_member_role_${(member.role ?? '').toLowerCase()}`)})`, avatarUrl: member.avatarURL ?? '', })); }, [adminMembers, allMembers, currentUserID, isShowUserPickerDialog, t]); if (!currentGroupID) { return null; } return ( {t('ChatSetting.group_management')} {hasGroupPermission(GroupPermission.SET_MEMBER_ROLE, GroupMemberRole.Owner, groupType) && ( handleAdminManagement('promote')} onRemoveButtonClick={() => handleAdminManagement('demote')} /> )} {hasGroupPermission(GroupPermission.MUTE_MEMBER, currentUserRole, groupType) && ( handleMuteManagement('mute')} onRemoveButtonClick={() => handleMuteManagement('unmute')} /> )} {hasGroupPermission(GroupPermission.MUTE_ALL_MEMBERS, currentUserRole, groupType) && isMuteAllMembers !== undefined && ( handleMuteAllToggle(!isMuteAllMembers)} /> {t('ChatSetting.mute_all_members_description')} )} setIsShowUserPickerDialog(false)} onCancel={() => setIsShowUserPickerDialog(false)} onConfirm={handleUserPickerConfirm} style={{ height: '80vh', }} > ); }); GroupManagement.displayName = 'GroupManagement'; export { GroupManagement, };