diff --git a/app/assets/javascripts/constants.js b/app/assets/javascripts/constants.js index f43a2d5d8ff..631968ff531 100644 --- a/app/assets/javascripts/constants.js +++ b/app/assets/javascripts/constants.js @@ -3,3 +3,5 @@ export const getModifierKey = (removeSuffix = false) => { const winKey = `Ctrl${removeSuffix ? '' : '+'}`; return window.gl?.client?.isMac ? '⌘' : winKey; }; + +export const PRELOAD_THROTTLE_TIMEOUT_MS = 4000; diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index f4008fe3cc9..ed2d1dbb7d2 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -5,6 +5,7 @@ import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_ import { disableButtonIfEmptyField } from '~/lib/utils/common_utils'; import dropzoneInput from './dropzone_input'; import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown'; +import { PRELOAD_THROTTLE_TIMEOUT_MS } from './constants'; export default class GLForm { /** @@ -17,10 +18,11 @@ export default class GLForm { * By default, the backend embeds these in the global object gl.GfmAutocomplete.dataSources. * Use this param to override them. */ - constructor(form, enableGFM = {}, forceNew = false, gfmDataSources = {}) { + constructor(form, enableGFM = {}, forceNew = false, gfmDataSources = {}, preloadMembers = false) { this.form = form; this.textarea = this.form.find('textarea.js-gfm-input'); this.enableGFM = { ...defaultAutocompleteConfig, ...enableGFM }; + this.preloadMembers = preloadMembers; // Disable autocomplete for keywords which do not have dataSources available let dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {}; @@ -68,6 +70,21 @@ export default class GLForm { ); this.autoComplete = new GfmAutoComplete(dataSources); this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM); + + if (this.preloadMembers && dataSources?.members) { + // for now the preload is only implemented for the members + // timeout helping to trottle the preloads in the case content_editor + // is set as main comment editor and support for rspec tests + // https://gitlab.com/gitlab-org/gitlab/-/issues/427437 + + requestIdleCallback(() => + setTimeout( + () => this.autoComplete?.fetchData($('.js-gfm-input'), '@'), + PRELOAD_THROTTLE_TIMEOUT_MS, + ), + ); + } + this.formDropzone = dropzoneInput(this.form, { parallelUploads: 1 }); if (this.form.is(':not(.js-no-autosize)')) { diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js index 265e2a2f880..c9e56a6161f 100644 --- a/app/assets/javascripts/mr_notes/init_notes.js +++ b/app/assets/javascripts/mr_notes/init_notes.js @@ -12,6 +12,7 @@ import discussionNavigator from '../notes/components/discussion_navigator.vue'; import NotesApp from '../notes/components/notes_app.vue'; import { getNotesFilterData } from '../notes/utils/get_notes_filter_data'; import initWidget from '../vue_merge_request_widget'; +import { MERGE_REQUEST_NOTEABLE_TYPE } from '../notes/constants'; export default () => { requestIdleCallback( @@ -45,6 +46,9 @@ export default () => { newCommentTemplatePath: notesDataset.newCommentTemplatePath, mrFilter: true, newCustomEmojiPath: notesDataset.newCustomEmojiPath, + preloadMembers: + notesDataset?.noteableType === MERGE_REQUEST_NOTEABLE_TYPE && + gon?.features?.preloadAutocompleteMembersIssuesMrs, }, data() { const noteableData = JSON.parse(notesDataset.noteableData); diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index f9fbe6659ee..cc9a3d87939 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -7,6 +7,7 @@ import { getLocationHash } from '~/lib/utils/url_utility'; import NotesApp from './components/notes_app.vue'; import { store } from './stores'; import { getNotesFilterData } from './utils/get_notes_filter_data'; +import { ISSUE_NOTEABLE_TYPE } from './constants'; export default ({ editorAiActions = [] } = {}) => { const el = document.getElementById('js-vue-notes'); @@ -63,6 +64,9 @@ export default ({ editorAiActions = [] } = {}) => { resourceGlobalId: convertToGraphQLId(noteableData.noteableType, noteableData.id), editorAiActions: editorAiActions.map((factory) => factory(noteableData)), newCustomEmojiPath: notesDataset.newCustomEmojiPath, + preloadMembers: + notesDataset.noteableType === ISSUE_NOTEABLE_TYPE && + gon?.features?.preloadAutocompleteMembersIssuesMrs, }, data() { return { diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index e80f5c7f092..034d591b6ca 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -33,6 +33,13 @@ export default { GlTooltip: GlTooltipDirective, }, mixins: [glFeatureFlagsMixin()], + inject: { + preloadMembers: { + type: Boolean, + required: false, + default: false, + }, + }, props: { /** * This prop should be bound to the value of the `