import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import './PublicRooms.scss'; import initMatrix from '../../../client/initMatrix'; import cons from '../../../client/state/cons'; import { selectRoom } from '../../../client/action/navigation'; import * as roomActions from '../../../client/action/room'; 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 RoomTile from '../../molecules/room-tile/RoomTile'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg'; const SEARCH_LIMIT = 20; function TryJoinWithAlias({ alias, onRequestClose }) { const [status, setStatus] = useState({ isJoining: false, error: null, roomId: null, tempRoomId: null, }); function handleOnRoomAdded(roomId) { if (status.tempRoomId !== null && status.tempRoomId !== roomId) return; setStatus({ isJoining: false, error: null, roomId, tempRoomId: null, }); } useEffect(() => { initMatrix.roomList.on(cons.events.roomList.ROOM_JOINED, handleOnRoomAdded); return () => { initMatrix.roomList.removeListener(cons.events.roomList.ROOM_JOINED, handleOnRoomAdded); }; }, [status]); async function joinWithAlias() { setStatus({ isJoining: true, error: null, roomId: null, tempRoomId: null, }); try { const roomId = await roomActions.join(alias, false); setStatus({ isJoining: true, error: null, roomId: null, tempRoomId: roomId, }); } catch (e) { setStatus({ isJoining: false, error: `Unable to join ${alias}. Either room is private or doesn't exist.`, roomId: null, tempRoomId: null, }); } } return (
{status.roomId === null && !status.isJoining && status.error === null && ( )} {status.isJoining && ( <> {`Joining ${alias}...`} )} {status.roomId !== null && ( )} {status.error !== null && {status.error}}
); } TryJoinWithAlias.propTypes = { alias: PropTypes.string.isRequired, onRequestClose: PropTypes.func.isRequired, }; function PublicRooms({ isOpen, searchTerm, onRequestClose }) { const [isSearching, updateIsSearching] = useState(false); const [isViewMore, updateIsViewMore] = useState(false); const [publicRooms, updatePublicRooms] = useState([]); const [nextBatch, updateNextBatch] = useState(undefined); const [searchQuery, updateSearchQuery] = useState({}); const [joiningRooms, updateJoiningRooms] = useState(new Set()); const roomNameRef = useRef(null); const hsRef = useRef(null); const userId = initMatrix.matrixClient.getUserId(); async function searchRooms(viewMore) { let inputRoomName = roomNameRef?.current?.value || searchTerm; let isInputAlias = false; if (typeof inputRoomName === 'string') { isInputAlias = inputRoomName[0] === '#' && inputRoomName.indexOf(':') > 1; } const hsFromAlias = (isInputAlias) ? inputRoomName.slice(inputRoomName.indexOf(':') + 1) : null; let inputHs = hsFromAlias || hsRef?.current?.value; if (typeof inputHs !== 'string') inputHs = userId.slice(userId.indexOf(':') + 1); if (typeof inputRoomName !== 'string') inputRoomName = ''; if (isSearching) return; if (viewMore !== true && inputRoomName === searchQuery.name && inputHs === searchQuery.homeserver ) return; updateSearchQuery({ name: inputRoomName, homeserver: inputHs, }); if (isViewMore !== viewMore) updateIsViewMore(viewMore); updateIsSearching(true); try { const result = await initMatrix.matrixClient.publicRooms({ server: inputHs, limit: SEARCH_LIMIT, since: viewMore ? nextBatch : undefined, include_all_networks: true, filter: { generic_search_term: inputRoomName, }, }); const totalRooms = viewMore ? publicRooms.concat(result.chunk) : result.chunk; updatePublicRooms(totalRooms); updateNextBatch(result.next_batch); updateIsSearching(false); updateIsViewMore(false); if (totalRooms.length === 0) { updateSearchQuery({ error: inputRoomName === '' ? `No public rooms on ${inputHs}` : `No result found for "${inputRoomName}" on ${inputHs}`, alias: isInputAlias ? inputRoomName : null, }); } } catch (e) { updatePublicRooms([]); let err = 'Something went wrong!'; if (e?.httpStatus >= 400 && e?.httpStatus < 500) { err = e.message; } updateSearchQuery({ error: err, alias: isInputAlias ? inputRoomName : null, }); updateIsSearching(false); updateNextBatch(undefined); updateIsViewMore(false); } } useEffect(() => { if (isOpen) searchRooms(); }, [isOpen]); function handleOnRoomAdded(roomId) { if (joiningRooms.has(roomId)) { joiningRooms.delete(roomId); updateJoiningRooms(new Set(Array.from(joiningRooms))); } } useEffect(() => { initMatrix.roomList.on(cons.events.roomList.ROOM_JOINED, handleOnRoomAdded); return () => { initMatrix.roomList.removeListener(cons.events.roomList.ROOM_JOINED, handleOnRoomAdded); }; }, [joiningRooms]); function handleViewRoom(roomId) { selectRoom(roomId); onRequestClose(); } function joinRoom(roomIdOrAlias) { joiningRooms.add(roomIdOrAlias); updateJoiningRooms(new Set(Array.from(joiningRooms))); roomActions.join(roomIdOrAlias, false); } function renderRoomList(rooms) { return rooms.map((room) => { const alias = typeof room.canonical_alias === 'string' ? room.canonical_alias : room.room_id; const name = typeof room.name === 'string' ? room.name : alias; const isJoined = initMatrix.roomList.rooms.has(room.room_id); return ( {isJoined && } {!isJoined && (joiningRooms.has(room.room_id) ? : )} )} /> ); }); } return ( } onRequestClose={onRequestClose} >
{ e.preventDefault(); searchRooms(); }}>
{ typeof searchQuery.name !== 'undefined' && isSearching && ( searchQuery.name === '' ? (
{`Loading public rooms from ${searchQuery.homeserver}...`}
) : (
{`Searching for "${searchQuery.name}" on ${searchQuery.homeserver}...`}
) ) } { typeof searchQuery.name !== 'undefined' && !isSearching && ( searchQuery.name === '' ? {`Public rooms on ${searchQuery.homeserver}.`} : {`Search result for "${searchQuery.name}" on ${searchQuery.homeserver}.`} ) } { searchQuery.error && ( <> {searchQuery.error} {typeof searchQuery.alias === 'string' && ( )} )}
{ publicRooms.length !== 0 && (
{ renderRoomList(publicRooms) }
)} { publicRooms.length !== 0 && publicRooms.length % SEARCH_LIMIT === 0 && (
{ isViewMore !== true && ( )} { isViewMore && }
)}
); } PublicRooms.defaultProps = { searchTerm: undefined, }; PublicRooms.propTypes = { isOpen: PropTypes.bool.isRequired, searchTerm: PropTypes.string, onRequestClose: PropTypes.func.isRequired, }; export default PublicRooms;