Files
cinny/src/app/organisms/room/PeopleDrawer.jsx
T

231 lines
7.5 KiB
React
Raw Normal View History

2021-10-22 20:02:01 +05:30
import React, {
useState, useEffect, useCallback, useRef,
} from 'react';
2021-07-28 18:45:52 +05:30
import PropTypes from 'prop-types';
import './PeopleDrawer.scss';
import initMatrix from '../../../client/initMatrix';
2021-10-18 17:25:52 +02:00
import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil';
2021-07-28 18:45:52 +05:30
import colorMXID from '../../../util/colorMXID';
2021-10-18 17:25:52 +02:00
import { openInviteUser, openProfileViewer } from '../../../client/action/navigation';
2021-10-21 17:50:49 +05:30
import AsyncSearch from '../../../util/AsyncSearch';
2021-07-28 18:45:52 +05:30
import Text from '../../atoms/text/Text';
import Header, { TitleWrapper } from '../../atoms/header/Header';
2021-10-22 20:02:01 +05:30
import RawIcon from '../../atoms/system-icons/RawIcon';
2021-07-28 18:45:52 +05:30
import IconButton from '../../atoms/button/IconButton';
import Button from '../../atoms/button/Button';
import ScrollView from '../../atoms/scroll/ScrollView';
import Input from '../../atoms/input/Input';
2021-10-23 15:27:54 +05:30
import SegmentedControl from '../../atoms/segmented-controls/SegmentedControls';
2021-07-28 18:45:52 +05:30
import PeopleSelector from '../../molecules/people-selector/PeopleSelector';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
2021-10-22 20:02:01 +05:30
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
2021-07-28 18:45:52 +05:30
2021-09-05 18:56:34 +05:30
function AtoZ(m1, m2) {
const aName = m1.name;
const bName = m2.name;
2021-07-28 18:45:52 +05:30
if (aName.toLowerCase() < bName.toLowerCase()) {
return -1;
}
if (aName.toLowerCase() > bName.toLowerCase()) {
return 1;
}
return 0;
}
function sortByPowerLevel(m1, m2) {
2021-09-05 18:56:34 +05:30
const pl1 = m1.powerLevel;
const pl2 = m2.powerLevel;
2021-07-28 18:45:52 +05:30
2021-09-05 18:56:34 +05:30
if (pl1 > pl2) return -1;
if (pl1 < pl2) return 1;
2021-07-28 18:45:52 +05:30
return 0;
}
2021-10-21 17:50:49 +05:30
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,
}));
}
2021-07-28 18:45:52 +05:30
2021-10-21 17:50:49 +05:30
const asyncSearch = new AsyncSearch();
2021-07-28 18:45:52 +05:30
function PeopleDrawer({ roomId }) {
const PER_PAGE_MEMBER = 50;
2021-10-21 17:50:49 +05:30
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
2021-07-28 18:45:52 +05:30
2021-10-21 17:50:49 +05:30
const [itemCount, setItemCount] = useState(PER_PAGE_MEMBER);
const [membership, setMembership] = useState('join');
const [memberList, setMemberList] = useState([]);
const [searchedMembers, setSearchedMembers] = useState(null);
2021-10-22 20:02:01 +05:30
const searchRef = useRef(null);
2021-10-21 17:50:49 +05:30
const getMembersWithMembership = useCallback(
(mship) => room.getMembersWithMembership(mship),
[roomId, membership],
);
2021-07-28 18:45:52 +05:30
function loadMorePeople() {
2021-10-21 17:50:49 +05:30
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);
2021-07-28 18:45:52 +05:30
}
2021-10-21 17:50:49 +05:30
function handleSearch(e) {
2021-10-22 20:02:01 +05:30
const term = e.target.value;
if (term === '' || term === undefined) {
searchRef.current.value = '';
searchRef.current.focus();
2021-10-21 17:50:49 +05:30
setSearchedMembers(null);
setItemCount(PER_PAGE_MEMBER);
2021-10-22 20:02:01 +05:30
} else asyncSearch.search(term);
2021-10-21 17:50:49 +05:30
}
useEffect(() => {
asyncSearch.setup(memberList, {
keys: ['name', 'username', 'userId'],
limit: PER_PAGE_MEMBER,
});
}, [memberList]);
2021-07-28 18:45:52 +05:30
useEffect(() => {
2021-10-29 18:11:02 +05:30
let isGettingMembers = true;
2021-12-10 10:51:32 +05:30
let isRoomChanged = false;
2021-10-29 18:11:02 +05:30
const updateMemberList = (event) => {
if (isGettingMembers) return;
if (event && event?.event?.room_id !== roomId) return;
2021-10-21 17:50:49 +05:30
setMemberList(
simplyfiMembers(
getMembersWithMembership(membership)
.sort(AtoZ).sort(sortByPowerLevel),
),
);
2021-10-29 18:11:02 +05:30
};
searchRef.current.value = '';
updateMemberList();
room.loadMembersIfNeeded().then(() => {
isGettingMembers = false;
if (isRoomChanged) return;
updateMemberList();
2021-07-28 18:45:52 +05:30
});
2021-10-21 17:50:49 +05:30
asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchData);
2021-10-29 18:11:02 +05:30
mx.on('RoomMember.membership', updateMemberList);
2021-07-28 18:45:52 +05:30
return () => {
isRoomChanged = true;
2021-10-21 17:50:49 +05:30
setMemberList([]);
setSearchedMembers(null);
setItemCount(PER_PAGE_MEMBER);
asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchData);
2021-10-29 18:11:02 +05:30
mx.removeListener('RoomMember.membership', updateMemberList);
2021-07-28 18:45:52 +05:30
};
2021-10-23 15:27:54 +05:30
}, [roomId, membership]);
useEffect(() => {
setMembership('join');
2021-07-28 18:45:52 +05:30
}, [roomId]);
2021-10-21 17:50:49 +05:30
const mList = searchedMembers !== null ? searchedMembers.data : memberList.slice(0, itemCount);
2021-07-28 18:45:52 +05:30
return (
<div className="people-drawer">
<Header>
<TitleWrapper>
2021-12-16 17:55:16 +05:30
<Text variant="s1" primary>
2021-07-28 18:45:52 +05:30
People
<Text className="people-drawer__member-count" variant="b3">{`${room.getJoinedMemberCount()} members`}</Text>
</Text>
</TitleWrapper>
<IconButton onClick={() => openInviteUser(roomId)} tooltip="Invite" src={AddUserIC} />
</Header>
<div className="people-drawer__content-wrapper">
<div className="people-drawer__scrollable">
<ScrollView autoHide>
<div className="people-drawer__content">
2021-10-23 15:27:54 +05:30
<SegmentedControl
selected={
(() => {
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]?.();
}}
/>
2021-07-28 18:45:52 +05:30
{
2021-10-21 17:50:49 +05:30
mList.map((member) => (
2021-07-28 18:45:52 +05:30
<PeopleSelector
key={member.userId}
2021-10-18 17:25:52 +02:00
onClick={() => openProfileViewer(member.userId, roomId)}
2021-10-21 17:50:49 +05:30
avatarSrc={member.avatarSrc}
name={member.name}
2021-07-28 18:45:52 +05:30
color={colorMXID(member.userId)}
2021-10-21 17:50:49 +05:30
peopleRole={member.peopleRole}
2021-07-28 18:45:52 +05:30
/>
))
}
2021-10-22 20:02:01 +05:30
{
2021-10-23 15:27:54 +05:30
(searchedMembers?.data.length === 0 || memberList.length === 0)
2021-10-22 20:02:01 +05:30
&& (
<div className="people-drawer__noresult">
<Text variant="b2">No result found!</Text>
</div>
)
}
2021-07-28 18:45:52 +05:30
<div className="people-drawer__load-more">
{
2021-10-22 20:02:01 +05:30
mList.length !== 0
&& memberList.length > itemCount
&& searchedMembers === null
&& (
2021-07-28 18:45:52 +05:30
<Button onClick={loadMorePeople}>View more</Button>
)
}
</div>
</div>
</ScrollView>
</div>
<div className="people-drawer__sticky">
<form onSubmit={(e) => e.preventDefault()} className="people-search">
2021-10-22 20:02:01 +05:30
<RawIcon size="small" src={SearchIC} />
<Input forwardRef={searchRef} type="text" onChange={handleSearch} placeholder="Search" required />
{
searchedMembers !== null
&& <IconButton onClick={handleSearch} size="small" src={CrossIC} />
}
2021-07-28 18:45:52 +05:30
</form>
</div>
</div>
</div>
);
}
PeopleDrawer.propTypes = {
roomId: PropTypes.string.isRequired,
};
export default PeopleDrawer;