From 735e96e5a0bc557a81426be82d2dce8cab31172f Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Mon, 12 Dec 2022 15:14:51 +1000 Subject: [PATCH] FIX: Ensure hashtag autocomplete is not behind keyboard in chat (#19419) We must set `treatAsTextarea` to true when using autocomplete in the chat composer, since it is at the bottom of the screen we always want to show it above the composer. This fixes the issue where the hashtag autocomplete results went behind the keyboard on mobile (which was not happening for mentions). --- .../discourse/app/components/d-editor.js | 8 ++++--- .../discourse/app/lib/hashtag-autocomplete.js | 22 +++++++++++++------ .../discourse/components/chat-composer.js | 9 +++++--- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/d-editor.js b/app/assets/javascripts/discourse/app/components/d-editor.js index 64175e030fa..a622955b4c6 100644 --- a/app/assets/javascripts/discourse/app/components/d-editor.js +++ b/app/assets/javascripts/discourse/app/components/d-editor.js @@ -464,9 +464,11 @@ export default Component.extend(TextareaTextManipulation, { this.site.hashtag_configurations["topic-composer"], this._$textarea, this.siteSettings, - (value) => { - this.set("value", value); - schedule("afterRender", this, this.focusTextArea); + { + afterComplete: (value) => { + this.set("value", value); + schedule("afterRender", this, this.focusTextArea); + }, } ); }, diff --git a/app/assets/javascripts/discourse/app/lib/hashtag-autocomplete.js b/app/assets/javascripts/discourse/app/lib/hashtag-autocomplete.js index 14b6e870a25..4d7129ea0c0 100644 --- a/app/assets/javascripts/discourse/app/lib/hashtag-autocomplete.js +++ b/app/assets/javascripts/discourse/app/lib/hashtag-autocomplete.js @@ -27,23 +27,30 @@ import { htmlSafe } from "@ember/template"; * @param {$Element} $textarea - jQuery element to use for the autocompletion * plugin to attach to, this is what will watch for the # matcher when the user is typing. * @param {Hash} siteSettings - The clientside site settings. - * @param {Function} afterComplete - Called with the selected autocomplete option once it is selected. + * @param {Function} autocompleteOptions - Options to pass to the jQuery plugin. Must at least include: + * + * - afterComplete - Called with the selected autocomplete option once it is selected. + * + * Can also include: + * + * - treatAsTextarea - Whether to anchor the autocompletion to the start of the input and + * ensure the popper is always on top. **/ export function setupHashtagAutocomplete( contextualHashtagConfiguration, $textArea, siteSettings, - afterComplete + autocompleteOptions = {} ) { if (siteSettings.enable_experimental_hashtag_autocomplete) { _setupExperimental( contextualHashtagConfiguration, $textArea, siteSettings, - afterComplete + autocompleteOptions ); } else { - _setup($textArea, siteSettings, afterComplete); + _setup($textArea, siteSettings, autocompleteOptions.afterComplete); } } @@ -123,13 +130,14 @@ function _setupExperimental( contextualHashtagConfiguration, $textArea, siteSettings, - afterComplete + autocompleteOptions ) { $textArea.autocomplete({ template: findRawTemplate("hashtag-autocomplete"), key: "#", - afterComplete, - treatAsTextarea: $textArea[0].tagName === "INPUT", + afterComplete: autocompleteOptions.afterComplete, + treatAsTextarea: autocompleteOptions.treatAsTextarea, + autoSelectFirstSuggestion: true, transformComplete: (obj) => obj.ref, dataSource: (term) => { if (term.match(/\s/)) { diff --git a/plugins/chat/assets/javascripts/discourse/components/chat-composer.js b/plugins/chat/assets/javascripts/discourse/components/chat-composer.js index 0edc60c7607..1ce08f09700 100644 --- a/plugins/chat/assets/javascripts/discourse/components/chat-composer.js +++ b/plugins/chat/assets/javascripts/discourse/components/chat-composer.js @@ -410,9 +410,12 @@ export default Component.extend(TextareaTextManipulation, { this.site.hashtag_configurations["chat-composer"], $textarea, this.siteSettings, - (value) => { - this.set("value", value); - return this._focusTextArea(); + { + treatAsTextarea: true, + afterComplete: (value) => { + this.set("value", value); + return this._focusTextArea(); + }, } ); },