Load mentionValues asynchronously (#36739)
Eliminate a few database queries on all issue and pull request pages by moving mention autocomplete data to async JSON endpoints fetched on-demand when the user types `@`. See https://github.com/go-gitea/gitea/pull/36739#issuecomment-3963184858 for the full table of affected pages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -38,6 +38,7 @@ export function initTextExpander(expander: TextExpanderElement) {
|
||||
if (!expander) return;
|
||||
|
||||
const textarea = expander.querySelector<HTMLTextAreaElement>('textarea')!;
|
||||
const mentionsUrl = expander.closest('[data-mentions-url]')?.getAttribute('data-mentions-url');
|
||||
|
||||
// help to fix the text-expander "multiword+promise" bug: do not show the popup when there is no "#" before current line
|
||||
const shouldShowIssueSuggestions = () => {
|
||||
@@ -83,36 +84,39 @@ export function initTextExpander(expander: TextExpanderElement) {
|
||||
|
||||
provide({matched: true, fragment: ul});
|
||||
} else if (key === '@') {
|
||||
const matches = matchMention(text);
|
||||
if (!matches.length) return provide({matched: false});
|
||||
provide((async (): Promise<TextExpanderResult> => {
|
||||
if (!mentionsUrl) return {matched: false};
|
||||
const matches = await matchMention(mentionsUrl, text);
|
||||
if (!matches.length) return {matched: false};
|
||||
|
||||
const ul = document.createElement('ul');
|
||||
ul.classList.add('suggestions');
|
||||
for (const {value, name, fullname, avatar} of matches) {
|
||||
const li = document.createElement('li');
|
||||
li.setAttribute('role', 'option');
|
||||
li.setAttribute('data-value', `${key}${value}`);
|
||||
const ul = document.createElement('ul');
|
||||
ul.classList.add('suggestions');
|
||||
for (const {value, name, fullname, avatar} of matches) {
|
||||
const li = document.createElement('li');
|
||||
li.setAttribute('role', 'option');
|
||||
li.setAttribute('data-value', `${key}${value}`);
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = avatar;
|
||||
li.append(img);
|
||||
const img = document.createElement('img');
|
||||
img.src = avatar;
|
||||
li.append(img);
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.classList.add('name');
|
||||
nameSpan.textContent = name;
|
||||
li.append(nameSpan);
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.classList.add('name');
|
||||
nameSpan.textContent = name;
|
||||
li.append(nameSpan);
|
||||
|
||||
if (fullname && fullname.toLowerCase() !== name) {
|
||||
const fullnameSpan = document.createElement('span');
|
||||
fullnameSpan.classList.add('fullname');
|
||||
fullnameSpan.textContent = fullname;
|
||||
li.append(fullnameSpan);
|
||||
if (fullname && fullname.toLowerCase() !== name) {
|
||||
const fullnameSpan = document.createElement('span');
|
||||
fullnameSpan.classList.add('fullname');
|
||||
fullnameSpan.textContent = fullname;
|
||||
li.append(fullnameSpan);
|
||||
}
|
||||
|
||||
ul.append(li);
|
||||
}
|
||||
|
||||
ul.append(li);
|
||||
}
|
||||
|
||||
provide({matched: true, fragment: ul});
|
||||
return {matched: true, fragment: ul};
|
||||
})());
|
||||
} else if (key === '#') {
|
||||
provide(debouncedIssueSuggestions(key, text));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {emojiKeys, emojiHTML, emojiString} from './emoji.ts';
|
||||
import {html, htmlRaw} from '../utils/html.ts';
|
||||
import {fetchMentions} from '../utils/match.ts';
|
||||
import type {TributeCollection} from 'tributejs';
|
||||
import type {MentionValue} from '../types.ts';
|
||||
import type {Mention} from '../types.ts';
|
||||
|
||||
export async function attachTribute(element: HTMLElement) {
|
||||
const {default: Tribute} = await import(/* webpackChunkName: "tribute" */'tributejs');
|
||||
const mentionsUrl = element.closest('[data-mentions-url]')?.getAttribute('data-mentions-url');
|
||||
|
||||
const emojiCollection: TributeCollection<string> = { // emojis
|
||||
trigger: ':',
|
||||
@@ -29,8 +31,10 @@ export async function attachTribute(element: HTMLElement) {
|
||||
},
|
||||
};
|
||||
|
||||
const mentionCollection: TributeCollection<MentionValue> = {
|
||||
values: window.config.mentionValues,
|
||||
const mentionCollection: TributeCollection<Mention> = {
|
||||
values: async (_query: string, cb: (matches: Mention[]) => void) => { // eslint-disable-line @typescript-eslint/no-misused-promises
|
||||
cb(mentionsUrl ? await fetchMentions(mentionsUrl) : []);
|
||||
},
|
||||
requireLeadingSpace: true,
|
||||
menuItemTemplate: (item) => {
|
||||
const fullNameHtml = item.original.fullname && item.original.fullname !== '' ? html`<span class="fullname">${item.original.fullname}</span>` : '';
|
||||
|
||||
Reference in New Issue
Block a user