154 lines
3.9 KiB
JavaScript
154 lines
3.9 KiB
JavaScript
import React, { memo, useState, useEffect, useRef } from 'react';
|
|
import Modal from 'react-modal';
|
|
import { useSelector, useDispatch } from 'react-redux';
|
|
import { FiUsers, FiX } from 'react-icons/fi';
|
|
import useModal from 'components/modals/useModal';
|
|
import Button from 'components/ui/Button';
|
|
import { join } from 'state/channels';
|
|
import { select } from 'state/tab';
|
|
import { searchChannels } from 'state/channelSearch';
|
|
import { linkify } from 'utils';
|
|
|
|
const Channel = memo(({ server, name, topic, userCount, joined }) => {
|
|
const dispatch = useDispatch();
|
|
|
|
const handleClick = () => dispatch(join([name], server));
|
|
|
|
return (
|
|
<div className="modal-channel-result">
|
|
<div className="modal-channel-result-header">
|
|
<h2 className="modal-channel-name" onClick={handleClick}>
|
|
{name}
|
|
</h2>
|
|
<FiUsers />
|
|
<span className="modal-channel-users">{userCount}</span>
|
|
{joined ? (
|
|
<span style={{ color: '#6bb758' }}>Joined</span>
|
|
) : (
|
|
<Button
|
|
className="modal-channel-button-join"
|
|
category="normal"
|
|
onClick={handleClick}
|
|
>
|
|
Join
|
|
</Button>
|
|
)}
|
|
</div>
|
|
<p className="modal-channel-topic">{linkify(topic)}</p>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
const AddChannel = () => {
|
|
const [modal, server, closeModal] = useModal('channel');
|
|
|
|
const channels = useSelector(state => state.channels);
|
|
const search = useSelector(state => state.channelSearch);
|
|
const dispatch = useDispatch();
|
|
const [q, setQ] = useState('');
|
|
|
|
const inputEl = useRef();
|
|
const resultsEl = useRef();
|
|
const prevSearch = useRef('');
|
|
|
|
useEffect(() => {
|
|
if (modal.isOpen) {
|
|
dispatch(searchChannels(server, ''));
|
|
setTimeout(() => inputEl.current.focus(), 0);
|
|
} else {
|
|
prevSearch.current = '';
|
|
setQ('');
|
|
}
|
|
}, [modal.isOpen]);
|
|
|
|
const handleSearch = e => {
|
|
let nextQ = e.target.value.trim().toLowerCase();
|
|
setQ(nextQ);
|
|
|
|
if (nextQ !== q) {
|
|
resultsEl.current.scrollTop = 0;
|
|
|
|
while (nextQ.charAt(0) === '#') {
|
|
nextQ = nextQ.slice(1);
|
|
}
|
|
|
|
if (nextQ !== prevSearch.current) {
|
|
prevSearch.current = nextQ;
|
|
dispatch(searchChannels(server, nextQ));
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleKey = e => {
|
|
if (e.key === 'Enter') {
|
|
let channel = e.target.value.trim();
|
|
|
|
if (channel !== '') {
|
|
closeModal(false);
|
|
|
|
if (channel.charAt(0) !== '#') {
|
|
channel = `#${channel}`;
|
|
}
|
|
|
|
dispatch(join([channel], server));
|
|
dispatch(select(server, channel));
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleLoadMore = () =>
|
|
dispatch(searchChannels(server, q, search.results.length));
|
|
|
|
let hasMore = !search.end;
|
|
if (hasMore) {
|
|
if (search.results.length < 10) {
|
|
hasMore = false;
|
|
} else if (
|
|
search.results.length > 10 &&
|
|
(search.results.length - 10) % 50 !== 0
|
|
) {
|
|
hasMore = false;
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Modal {...modal}>
|
|
<div className="modal-channel-input-wrap">
|
|
<input
|
|
ref={inputEl}
|
|
type="text"
|
|
value={q}
|
|
placeholder="Enter channel name"
|
|
onKeyDown={handleKey}
|
|
onChange={handleSearch}
|
|
/>
|
|
<Button
|
|
icon={FiX}
|
|
className="modal-close modal-channel-close"
|
|
onClick={closeModal}
|
|
/>
|
|
</div>
|
|
<div ref={resultsEl} className="modal-channel-results">
|
|
{search.results.map(channel => (
|
|
<Channel
|
|
key={`${server} ${channel.name}`}
|
|
server={server}
|
|
joined={channels[server]?.[channel.name]?.joined}
|
|
{...channel}
|
|
/>
|
|
))}
|
|
{hasMore && (
|
|
<Button
|
|
className="modal-channel-button-more"
|
|
onClick={handleLoadMore}
|
|
>
|
|
Load more
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default AddChannel;
|