diff --git a/frontend/src/Components/Page/Header/ArtistSearchInput.css b/frontend/src/Components/Page/Header/ArtistSearchInput.css index 95a796c80..bb2b5193c 100644 --- a/frontend/src/Components/Page/Header/ArtistSearchInput.css +++ b/frontend/src/Components/Page/Header/ArtistSearchInput.css @@ -4,15 +4,15 @@ } .loading { - margin-top: 18px; - margin-bottom: 18px; - text-align: center; + position: absolute; + display: inline-block; + margin-left: 5px; } .ripple { composes: ripple from '~Components/Loading/LoadingIndicator.css'; - border: 2px solid var(--toolbarColor); + border: 1px solid var(--toolbarColor); } .input { @@ -91,7 +91,7 @@ } .addNewArtistSuggestion { - padding: 0 3px; + padding: 5px 3px; cursor: pointer; } diff --git a/frontend/src/Components/Page/Header/ArtistSearchInput.js b/frontend/src/Components/Page/Header/ArtistSearchInput.js index 948c8b0b0..0abb555e0 100644 --- a/frontend/src/Components/Page/Header/ArtistSearchInput.js +++ b/frontend/src/Components/Page/Header/ArtistSearchInput.js @@ -10,9 +10,7 @@ import ArtistSearchResult from './ArtistSearchResult'; import FuseWorker from './fuse.worker'; import styles from './ArtistSearchInput.css'; -const LOADING_TYPE = 'suggestionsLoading'; const ADD_NEW_TYPE = 'addNew'; -const workerInstance = new FuseWorker(); class ArtistSearchInput extends Component { @@ -23,6 +21,7 @@ class ArtistSearchInput extends Component { super(props, context); this._autosuggest = null; + this._worker = null; this.state = { value: '', @@ -32,7 +31,23 @@ class ArtistSearchInput extends Component { componentDidMount() { this.props.bindShortcut(shortcuts.ARTIST_SEARCH_INPUT.key, this.focusInput); - workerInstance.addEventListener('message', this.onSuggestionsReceived, false); + } + + componentWillUnmount() { + if (this._worker) { + this._worker.removeEventListener('message', this.onSuggestionsReceived, false); + this._worker.terminate(); + this._worker = null; + } + } + + getWorker() { + if (!this._worker) { + this._worker = new FuseWorker(); + this._worker.addEventListener('message', this.onSuggestionsReceived, false); + } + + return this._worker; } // @@ -55,6 +70,15 @@ class ArtistSearchInput extends Component { return (
{section.title} + + { + section.loading && + + }
); } @@ -72,16 +96,6 @@ class ArtistSearchInput extends Component { ); } - if (item.type === LOADING_TYPE) { - return ( - - ); - } - return ( { + if (event.shiftKey || event.altKey || event.ctrlKey) { + return; + } + + if (event.key === 'Escape') { + this.reset(); + return; + } + if (event.key !== 'Tab' && event.key !== 'Enter') { return; } @@ -154,35 +178,74 @@ class ArtistSearchInput extends Component { }; onSuggestionsFetchRequested = ({ value }) => { - this.setState({ - suggestions: [ - { - type: LOADING_TYPE, - title: value - } - ] - }); + if (!this.state.loading) { + this.setState({ + loading: true + }); + } + this.requestSuggestions(value); }; requestSuggestions = _.debounce((value) => { - const payload = { - value, - artists: this.props.artists - }; + if (!this.state.loading) { + return; + } - workerInstance.postMessage(payload); + const requestLoading = this.state.requestLoading; + + this.setState({ + requestValue: value, + requestLoading: true + }); + + if (!requestLoading) { + const payload = { + value, + artists: this.props.artists + }; + + this.getWorker().postMessage(payload); + } }, 250); onSuggestionsReceived = (message) => { - this.setState({ - suggestions: message.data - }); + const { + value, + suggestions + } = message.data; + + if (!this.state.loading) { + this.setState({ + requestValue: null, + requestLoading: false + }); + } else if (value === this.state.requestValue) { + this.setState({ + suggestions, + requestValue: null, + requestLoading: false, + loading: false + }); + } else { + this.setState({ + suggestions, + requestLoading: true + }); + + const payload = { + value: this.state.requestValue, + artists: this.props.artists + }; + + this.getWorker().postMessage(payload); + } }; onSuggestionsClearRequested = () => { this.setState({ - suggestions: [] + suggestions: [], + loading: false }); }; @@ -200,14 +263,16 @@ class ArtistSearchInput extends Component { render() { const { value, + loading, suggestions } = this.state; const suggestionGroups = []; - if (suggestions.length) { + if (suggestions.length || loading) { suggestionGroups.push({ title: 'Existing Artist', + loading, suggestions }); } diff --git a/frontend/src/Components/Page/Header/fuse.worker.js b/frontend/src/Components/Page/Header/fuse.worker.js index 76e655a1b..3b4b13161 100644 --- a/frontend/src/Components/Page/Header/fuse.worker.js +++ b/frontend/src/Components/Page/Header/fuse.worker.js @@ -3,9 +3,9 @@ import Fuse from 'fuse.js'; const fuseOptions = { shouldSort: true, includeMatches: true, + ignoreLocation: true, threshold: 0.3, - location: 0, - distance: 100, + maxPatternLength: 32, minMatchCharLength: 1, keys: [ 'artistName', @@ -47,7 +47,7 @@ function getSuggestions(artists, value) { return suggestions; } -self.addEventListener('message', (e) => { +onmessage = function(e) { if (!e) { return; } @@ -57,5 +57,12 @@ self.addEventListener('message', (e) => { value } = e.data; - self.postMessage(getSuggestions(artists, value)); -}); + const suggestions = getSuggestions(artists, value); + + const results = { + value, + suggestions + }; + + self.postMessage(results); +};