import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import './InviteUser.scss'; import initMatrix from '../../../client/initMatrix'; import cons from '../../../client/state/cons'; import * as roomActions from '../../../client/action/room'; import { selectRoom } from '../../../client/action/navigation'; import Text from '../../atoms/text/Text'; import Button from '../../atoms/button/Button'; import IconButton from '../../atoms/button/IconButton'; import Spinner from '../../atoms/spinner/Spinner'; import Input from '../../atoms/input/Input'; import PopupWindow from '../../molecules/popup-window/PopupWindow'; import ChannelTile from '../../molecules/channel-tile/ChannelTile'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import UserIC from '../../../../public/res/ic/outlined/user.svg'; function InviteUser({ isOpen, roomId, onRequestClose }) { const [isSearching, updateIsSearching] = useState(false); const [searchQuery, updateSearchQuery] = useState({}); const [users, updateUsers] = useState([]); const [procUsers, updateProcUsers] = useState(new Set()); // proc stands for processing. const [procUserError, updateUserProcError] = useState(new Map()); const [createdDM, updateCreatedDM] = useState(new Map()); const [roomIdToUserId, updateRoomIdToUserId] = useState(new Map()); const [invitedUserIds, updateInvitedUserIds] = useState(new Set()); const usernameRef = useRef(null); const mx = initMatrix.matrixClient; function getMapCopy(myMap) { const newMap = new Map(); myMap.forEach((data, key) => { newMap.set(key, data); }); return newMap; } function addUserToProc(userId) { procUsers.add(userId); updateProcUsers(new Set(Array.from(procUsers))); } function deleteUserFromProc(userId) { procUsers.delete(userId); updateProcUsers(new Set(Array.from(procUsers))); } function onDMCreated(newRoomId) { const myDMPartnerId = roomIdToUserId.get(newRoomId); if (typeof myDMPartnerId === 'undefined') return; createdDM.set(myDMPartnerId, newRoomId); roomIdToUserId.delete(newRoomId); deleteUserFromProc(myDMPartnerId); updateCreatedDM(getMapCopy(createdDM)); updateRoomIdToUserId(getMapCopy(roomIdToUserId)); } useEffect(() => () => { updateIsSearching(false); updateSearchQuery({}); updateUsers([]); updateProcUsers(new Set()); updateUserProcError(new Map()); updateCreatedDM(new Map()); updateRoomIdToUserId(new Map()); updateInvitedUserIds(new Set()); }, [isOpen]); useEffect(() => { initMatrix.roomList.on(cons.events.roomList.ROOM_CREATED, onDMCreated); return () => { initMatrix.roomList.removeListener(cons.events.roomList.ROOM_CREATED, onDMCreated); }; }, [isOpen, procUsers, createdDM, roomIdToUserId]); async function searchUser() { const inputUsername = usernameRef.current.value.trim(); if (isSearching || inputUsername === '' || inputUsername === searchQuery.username) return; const isInputUserId = inputUsername[0] === '@' && inputUsername.indexOf(':') > 1; updateIsSearching(true); updateSearchQuery({ username: inputUsername }); if (isInputUserId) { try { const result = await mx.getProfileInfo(inputUsername); updateUsers([{ user_id: inputUsername, display_name: result.displayname, avatar_url: result.avatar_url, }]); } catch (e) { updateSearchQuery({ error: `${inputUsername} not found!` }); } } else { try { const result = await mx.searchUserDirectory({ term: inputUsername, limit: 20, }); if (result.results.length === 0) { updateSearchQuery({ error: `No matches found for "${inputUsername}"!` }); updateIsSearching(false); return; } updateUsers(result.results); } catch (e) { updateSearchQuery({ error: 'Something went wrong!' }); } } updateIsSearching(false); } async function createDM(userId) { if (mx.getUserId() === userId) return; try { addUserToProc(userId); procUserError.delete(userId); updateUserProcError(getMapCopy(procUserError)); const result = await roomActions.create({ isPublic: false, isEncrypted: true, isDirect: true, invite: [userId], }); roomIdToUserId.set(result.room_id, userId); updateRoomIdToUserId(getMapCopy(roomIdToUserId)); } catch (e) { deleteUserFromProc(userId); if (typeof e.message === 'string') procUserError.set(userId, e.message); else procUserError.set(userId, 'Something went wrong!'); updateUserProcError(getMapCopy(procUserError)); } } async function inviteToRoom(userId) { if (typeof roomId === 'undefined') return; try { addUserToProc(userId); procUserError.delete(userId); updateUserProcError(getMapCopy(procUserError)); await roomActions.invite(roomId, userId); invitedUserIds.add(userId); updateInvitedUserIds(new Set(Array.from(invitedUserIds))); deleteUserFromProc(userId); } catch (e) { deleteUserFromProc(userId); if (typeof e.message === 'string') procUserError.set(userId, e.message); else procUserError.set(userId, 'Something went wrong!'); updateUserProcError(getMapCopy(procUserError)); } } function renderUserList() { const renderOptions = (userId) => { const messageJSX = (message, isPositive) => {message}; if (mx.getUserId() === userId) return null; if (procUsers.has(userId)) { return ; } if (createdDM.has(userId)) { // eslint-disable-next-line max-len return ; } if (invitedUserIds.has(userId)) { return messageJSX('Invited', true); } if (typeof roomId === 'string') { const member = mx.getRoom(roomId).getMember(userId); if (member !== null) { const userMembership = member.membership; switch (userMembership) { case 'join': return messageJSX('Already joined', true); case 'invite': return messageJSX('Already Invited', true); case 'ban': return messageJSX('Banned', false); default: } } } return (typeof roomId === 'string') ? : ; }; const renderError = (userId) => { if (!procUserError.has(userId)) return null; return {procUserError.get(userId)}; }; return users.map((user) => { const userId = user.user_id; const name = typeof user.display_name === 'string' ? user.display_name : userId; return ( ); }); } return ( } onRequestClose={onRequestClose} >
{ e.preventDefault(); searchUser(); }}>
{ typeof searchQuery.username !== 'undefined' && isSearching && (
{`Searching for user "${searchQuery.username}"...`}
) } { typeof searchQuery.username !== 'undefined' && !isSearching && ( {`Search result for user "${searchQuery.username}"`} ) } { searchQuery.error && {searchQuery.error} }
{ users.length !== 0 && (
{renderUserList()}
)}
); } InviteUser.defaultProps = { roomId: undefined, }; InviteUser.propTypes = { isOpen: PropTypes.bool.isRequired, roomId: PropTypes.string, onRequestClose: PropTypes.func.isRequired, }; export default InviteUser;