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}
>
{
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;