]> git.localhorst.tv Git - alttp.git/blobdiff - resources/js/components/common/DiscordChannelSelect.js
respond to whispers
[alttp.git] / resources / js / components / common / DiscordChannelSelect.js
index 400ab9f687010ab871486b552c0e89faf23217bd..01ee3b0fa534a2c1cc8474ad7f6da775995924e5 100644 (file)
@@ -1,25 +1,44 @@
 import axios from 'axios';
 import PropTypes from 'prop-types';
 import React, { useCallback, useEffect, useState } from 'react';
-import { Form } from 'react-bootstrap';
-import { withTranslation } from 'react-i18next';
+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';
-import i18n from '../../i18n';
 
 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('mousedown', handleEventOutside, true);
+               document.addEventListener('focus', handleEventOutside, true);
+               return () => {
+                       document.removeEventListener('mousedown', handleEventOutside, true);
+                       document.removeEventListener('focus', handleEventOutside, true);
+               };
+       }, []);
 
        let ctrl = null;
-       const fetch = useCallback(debounce(async (guild, types) => {
+       const fetch = useCallback(debounce(async (guild, phrase, types) => {
                if (ctrl) {
                        ctrl.abort();
                }
@@ -27,6 +46,7 @@ const DiscordChannelSelect = ({
                try {
                        const response = await axios.get(`/api/discord-guilds/${guild}/channels`, {
                                params: {
+                                       phrase,
                                        types,
                                },
                                signal: ctrl.signal,
@@ -43,22 +63,66 @@ const DiscordChannelSelect = ({
        }, 300), []);
 
        useEffect(() => {
-               fetch(guild, types);
-       }, [guild, ...types]);
+               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]);
 
-       return <Form.Select
-               isInvalid={isInvalid}
-               name={name}
-               onBlur={onBlur}
-               onChange={onChange}
-               type="search"
-               value={value}
-       >
-               <option value="">{i18n.t('tournaments.discordNoCategory')}</option>
-               {results && results.length ? results.map(result =>
-                       <option key={result.id} value={result.channel_id}>{result.name}</option>
-               ) : null}
-       </Form.Select>;
+       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 = {
@@ -71,4 +135,4 @@ DiscordChannelSelect.propTypes = {
        value: PropTypes.string,
 };
 
-export default withTranslation()(DiscordChannelSelect);
+export default DiscordChannelSelect;