import React, { useState, useEffect, useCallback, useRef, } from 'react'; import PropTypes from 'prop-types'; import './PeopleDrawer.scss'; import initMatrix from '../../../client/initMatrix'; import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil'; import colorMXID from '../../../util/colorMXID'; import { openInviteUser, openProfileViewer } from '../../../client/action/navigation'; import AsyncSearch from '../../../util/AsyncSearch'; import Text from '../../atoms/text/Text'; import Header, { TitleWrapper } from '../../atoms/header/Header'; import RawIcon from '../../atoms/system-icons/RawIcon'; import IconButton from '../../atoms/button/IconButton'; import Button from '../../atoms/button/Button'; import ScrollView from '../../atoms/scroll/ScrollView'; import Input from '../../atoms/input/Input'; import SegmentedControl from '../../atoms/segmented-controls/SegmentedControls'; import PeopleSelector from '../../molecules/people-selector/PeopleSelector'; import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg'; import SearchIC from '../../../../public/res/ic/outlined/search.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; function AtoZ(m1, m2) { const aName = m1.name; const bName = m2.name; if (aName.toLowerCase() < bName.toLowerCase()) { return -1; } if (aName.toLowerCase() > bName.toLowerCase()) { return 1; } return 0; } function sortByPowerLevel(m1, m2) { const pl1 = m1.powerLevel; const pl2 = m2.powerLevel; if (pl1 > pl2) return -1; if (pl1 < pl2) return 1; return 0; } function simplyfiMembers(members) { const mx = initMatrix.matrixClient; return members.map((member) => ({ userId: member.userId, name: getUsernameOfRoomMember(member), username: member.userId.slice(1, member.userId.indexOf(':')), avatarSrc: member.getAvatarUrl(mx.baseUrl, 24, 24, 'crop'), peopleRole: getPowerLabel(member.powerLevel), powerLevel: members.powerLevel, })); } const asyncSearch = new AsyncSearch(); function PeopleDrawer({ roomId }) { const PER_PAGE_MEMBER = 50; const mx = initMatrix.matrixClient; const room = mx.getRoom(roomId); const canInvite = room?.canInvite(mx.getUserId()); const [itemCount, setItemCount] = useState(PER_PAGE_MEMBER); const [membership, setMembership] = useState('join'); const [memberList, setMemberList] = useState([]); const [searchedMembers, setSearchedMembers] = useState(null); const searchRef = useRef(null); const getMembersWithMembership = useCallback( (mship) => room.getMembersWithMembership(mship), [roomId, membership], ); function loadMorePeople() { setItemCount(itemCount + PER_PAGE_MEMBER); } function handleSearchData(data) { // NOTICE: data is passed as object property // because react sucks at handling state update with array. setSearchedMembers({ data }); setItemCount(PER_PAGE_MEMBER); } function handleSearch(e) { const term = e.target.value; if (term === '' || term === undefined) { searchRef.current.value = ''; searchRef.current.focus(); setSearchedMembers(null); setItemCount(PER_PAGE_MEMBER); } else asyncSearch.search(term); } useEffect(() => { asyncSearch.setup(memberList, { keys: ['name', 'username', 'userId'], limit: PER_PAGE_MEMBER, }); }, [memberList]); useEffect(() => { let isGettingMembers = true; let isRoomChanged = false; const updateMemberList = (event) => { if (isGettingMembers) return; if (event && event?.getRoomId() !== roomId) return; setMemberList( simplyfiMembers( getMembersWithMembership(membership) .sort(AtoZ).sort(sortByPowerLevel), ), ); }; searchRef.current.value = ''; updateMemberList(); room.loadMembersIfNeeded().then(() => { isGettingMembers = false; if (isRoomChanged) return; updateMemberList(); }); asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchData); mx.on('RoomMember.membership', updateMemberList); mx.on('RoomMember.powerLevel', updateMemberList); return () => { isRoomChanged = true; setMemberList([]); setSearchedMembers(null); setItemCount(PER_PAGE_MEMBER); asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData); mx.removeListener('RoomMember.membership', updateMemberList); mx.removeListener('RoomMember.powerLevel', updateMemberList); }; }, [roomId, membership]); useEffect(() => { setMembership('join'); }, [roomId]); const mList = searchedMembers !== null ? searchedMembers.data : memberList.slice(0, itemCount); return (
People {`${room.getJoinedMemberCount()} members`} openInviteUser(roomId)} tooltip="Invite" src={AddUserIC} disabled={!canInvite} />
{ const getSegmentIndex = { join: 0, invite: 1, ban: 2, }; return getSegmentIndex[membership]; })() } segments={[{ text: 'Joined' }, { text: 'Invited' }, { text: 'Banned' }]} onSelect={(index) => { const selectSegment = [ () => setMembership('join'), () => setMembership('invite'), () => setMembership('ban'), ]; selectSegment[index]?.(); }} /> { mList.map((member) => ( openProfileViewer(member.userId, roomId)} avatarSrc={member.avatarSrc} name={member.name} color={colorMXID(member.userId)} peopleRole={member.peopleRole} /> )) } { (searchedMembers?.data.length === 0 || memberList.length === 0) && (
No result found!
) }
{ mList.length !== 0 && memberList.length > itemCount && searchedMembers === null && ( ) }
e.preventDefault()} className="people-search"> { searchedMembers !== null && }
); } PeopleDrawer.propTypes = { roomId: PropTypes.string.isRequired, }; export default PeopleDrawer;