+++ /dev/null
-import axios from 'axios';
-import PropTypes from 'prop-types';
-import React, { useCallback, useEffect, useState } from 'react';
-import { Alert, Button, Form, ListGroup } from 'react-bootstrap';
-import { useTranslation } from 'react-i18next';
-
-import Icon from './Icon';
-import ChannelBox from '../discord-guilds/ChannelBox';
-import debounce from '../../helpers/debounce';
-
-const DiscordChannelSelect = ({
- guild,
- isInvalid,
- name,
- onBlur,
- onChange,
- types,
- value,
-}) => {
- const [resolved, setResolved] = useState(null);
- const [results, setResults] = useState([]);
- const [search, setSearch] = useState('');
- const [showResults, setShowResults] = useState(false);
-
- const ref = React.useRef(null);
- const { t } = useTranslation();
-
- useEffect(() => {
- const handleEventOutside = e => {
- if (ref.current && !ref.current.contains(e.target)) {
- setShowResults(false);
- }
- };
- document.addEventListener('click', handleEventOutside, true);
- document.addEventListener('focus', handleEventOutside, true);
- return () => {
- document.removeEventListener('click', handleEventOutside, true);
- document.removeEventListener('focus', handleEventOutside, true);
- };
- }, []);
-
- let ctrl = null;
- const fetch = useCallback(debounce(async (guild, phrase, types) => {
- if (ctrl) {
- ctrl.abort();
- }
- ctrl = new AbortController();
- try {
- const response = await axios.get(`/api/discord-guilds/${guild}/channels`, {
- params: {
- phrase,
- types,
- },
- signal: ctrl.signal,
- });
- ctrl = null;
- setResults(response.data);
- } catch (e) {
- ctrl = null;
- console.error(e);
- }
- return () => {
- if (ctrl) ctrl.abort();
- };
- }, 300), []);
-
- useEffect(() => {
- fetch(guild, search, types);
- }, [guild, search, ...types]);
-
- useEffect(() => {
- if (value) {
- axios
- .get(`/api/discord-channels/${value}`)
- .then(response => {
- setResolved(response.data);
- });
- } else {
- setResolved(null);
- }
- }, [value]);
-
- if (value) {
- return <div className="d-flex align-items-center justify-content-between">
- <span>{resolved ? <ChannelBox channel={resolved} /> : value}</span>
- <Button
- className="ms-2"
- onClick={() => onChange({ guild: null, target: { name, value: '' }})}
- title={t('button.unset')}
- variant="outline-danger"
- >
- <Icon.REMOVE title="" />
- </Button>
- </div>;
- }
- return <div className={`discord-select ${showResults ? 'expanded' : 'collapsed'}`} ref={ref}>
- <Form.Control
- className="search-input"
- name={Math.random().toString(20).substr(2, 10)}
- onChange={e => setSearch(e.target.value)}
- onFocus={() => setShowResults(true)}
- type="search"
- value={search}
- />
- <div className="search-results-holder">
- {results.length ?
- <ListGroup className="search-results">
- {results.map(result =>
- <ListGroup.Item
- action
- key={result.id}
- onClick={() => onChange({
- channel: result,
- target: { name, value: result.channel_id },
- })}
- >
- <ChannelBox channel={result} />
- </ListGroup.Item>
- )}
- </ListGroup>
- :
- <Alert className="search-results" variant="info">
- {t('search.noResults')}
- </Alert>
- }
- </div>
- </div>;
-};
-
-DiscordChannelSelect.propTypes = {
- guild: PropTypes.string,
- isInvalid: PropTypes.bool,
- name: PropTypes.string,
- onBlur: PropTypes.func,
- onChange: PropTypes.func,
- types: PropTypes.arrayOf(PropTypes.number),
- value: PropTypes.string,
-};
-
-export default DiscordChannelSelect;