From de81188b13e0d3bef5910ed91a1c53d896da0f93 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 28 Jun 2017 17:27:21 +0100 Subject: [PATCH] Do debouncing for autocomplete in a sane way - Fixes https://github.com/vector-im/riot-web/issues/4419 - Fixes https://github.com/matrix-org/matrix-react-sdk/pull/518#issuecomment-285901871 - Fixes https://github.com/matrix-org/matrix-react-sdk/pull/518#issuecomment-285910503 - Fixes bug where the setting being used was the `autocompleteDelay` "syncedSetting" when it should have been the "localSetting" (so the setting being used was always the default) --- src/components/views/rooms/Autocomplete.js | 72 +++++++++++++--------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index d591e4f6c2..dd6d9d1ae9 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -40,25 +40,51 @@ export default class Autocomplete extends React.Component { }; } - async componentWillReceiveProps(props, state) { - if (props.query === this.props.query) { - return null; - } - - return await this.complete(props.query, props.selection); - } - - async complete(query, selection) { - let forceComplete = this.state.forceComplete; - const completionPromise = getCompletions(query, selection, forceComplete); - this.completionPromise = completionPromise; - const completions = await this.completionPromise; - - // There's a newer completion request, so ignore results. - if (completionPromise !== this.completionPromise) { + componentWillReceiveProps(newProps, state) { + // Query hasn't changed so don't try to complete it + if (newProps.query === this.props.query) { return; } + this.complete(newProps.query, newProps.selection); + } + + complete(query, selection) { + if (this.debounceCompletionsRequest) { + clearTimeout(this.debounceCompletionsRequest); + } + if (query === "") { + this.setState({ + // Clear displayed completions + completions: [], + completionList: [], + // Reset selected completion + selectionOffset: COMPOSER_SELECTED, + // Hide the autocomplete box + hide: true, + }); + return Q(null); + } + let autocompleteDelay = UserSettingsStore.getLocalSetting('autocompleteDelay', 200); + + // Don't debounce if we are already showing completions + if (this.state.completions.length > 0) { + autocompleteDelay = 0; + } + + const deferred = Q.defer(); + this.debounceCompletionsRequest = setTimeout(() => { + getCompletions( + query, selection, this.state.forceComplete, + ).then((completions) => { + this.processCompletions(completions); + deferred.resolve(); + }); + }, autocompleteDelay); + return deferred.promise; + } + + processCompletions(completions) { const completionList = flatMap(completions, (provider) => provider.completions); // Reset selection when completion list becomes empty. @@ -88,23 +114,13 @@ export default class Autocomplete extends React.Component { hide = false; } - const autocompleteDelay = UserSettingsStore.getSyncedSetting('autocompleteDelay', 200); - - // We had no completions before, but do now, so we should apply our display delay here - if (this.state.completionList.length === 0 && completionList.length > 0 && - !forceComplete && autocompleteDelay > 0) { - await Q.delay(autocompleteDelay); - } - - // Force complete is turned off each time since we can't edit the query in that case - forceComplete = false; - this.setState({ completions, completionList, selectionOffset, hide, - forceComplete, + // Force complete is turned off each time since we can't edit the query in that case + forceComplete: false, }); }