[Glitch] Cancel emoji search requests

Port dca7bc42d25a55351c14861ea9c68229aaf54397 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
Echo 2026-07-28 10:35:21 +02:00 committed by Tarrien
parent 52eceb8168
commit a3eb0b0d9d
4 changed files with 81 additions and 36 deletions

View File

@ -20,6 +20,8 @@ import { updateTimeline } from './timelines';
let fetchComposeSuggestionsAccountsController; let fetchComposeSuggestionsAccountsController;
/** @type {AbortController | undefined} */ /** @type {AbortController | undefined} */
let fetchComposeSuggestionsTagsController; let fetchComposeSuggestionsTagsController;
/** @type {AbortController | undefined} */
let searchComposeSuggestionsEmojiController;
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
@ -531,9 +533,8 @@ export function undoUploadCompose(media_id) {
} }
export function clearComposeSuggestions() { export function clearComposeSuggestions() {
if (fetchComposeSuggestionsAccountsController) { fetchComposeSuggestionsAccountsController?.abort();
fetchComposeSuggestionsAccountsController.abort(); searchComposeSuggestionsEmojiController?.abort();
}
return { return {
type: COMPOSE_SUGGESTIONS_CLEAR, type: COMPOSE_SUGGESTIONS_CLEAR,
}; };
@ -566,12 +567,25 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, token) => {
}); });
}, 200, { leading: true, trailing: true }); }, 200, { leading: true, trailing: true });
const fetchComposeSuggestionsEmojis = async (dispatch, token) => { const fetchComposeSuggestionsEmojis = (dispatch, token) => {
// Right now we are hard-coding the locale to English since the picker search only supports English. dispatch(clearComposeSuggestions());
// Once we replace the legacy picker we can remove this and use the actual locale of the user. searchComposeSuggestionsEmojiController = new AbortController();
const results = await emojiMartSearch(token, 'en', 5);
dispatch(readyComposeSuggestionsEmojis(token, results)); void emojiMartSearch({
}; token,
// Right now we are hard-coding the locale to English since the picker search only supports English.
// Once we replace the legacy picker we can remove this and use the actual locale of the user.
locale: 'en',
limit: 5,
signal: searchComposeSuggestionsEmojiController.signal,
}).then((results) => {
if (results) {
dispatch(readyComposeSuggestionsEmojis(token, results));
}
}).finally(() => {
searchComposeSuggestionsEmojiController = undefined;
});
}
const fetchComposeSuggestionsTags = throttle((dispatch, token) => { const fetchComposeSuggestionsTags = throttle((dispatch, token) => {
if (fetchComposeSuggestionsTagsController) { if (fetchComposeSuggestionsTagsController) {

View File

@ -22,34 +22,50 @@ type LegacyEmoji =
}; };
// Replicates the old legacy search function. // Replicates the old legacy search function.
export async function emojiMartSearch( export async function emojiMartSearch({
token: string, token,
locale: string, locale,
limit = 5, limit = 5,
): Promise<LegacyEmoji[]> { signal,
const query = token.replace(':', '').trim(); }: {
if (!query.length) { token: string;
return []; locale: string;
limit?: number;
signal?: AbortSignal;
}): Promise<LegacyEmoji[] | null> {
try {
const query = token.replace(':', '').trim();
if (!query.length) {
return [];
}
const cacheKey = `${query}|${locale}|${limit}`;
const cachedResult = searchCache.get(cacheKey);
if (cachedResult) {
return cachedResult;
}
const results = await search({
query,
locale,
limit,
signal,
});
const legacyResults = results.map((emoji) =>
'shortcode' in emoji
? ({ id: emoji.shortcode, custom: true } as const)
: {
id: emoji.label.replaceAll(' ', '_').toLowerCase(),
native: emoji.unicode,
},
);
searchCache.set(cacheKey, legacyResults);
return legacyResults;
} catch {
log('aborted search for "%s"', token);
return null;
} }
const cacheKey = `${query}|${locale}|${limit}`;
const cachedResult = searchCache.get(cacheKey);
if (cachedResult) {
return cachedResult;
}
const results = await search({ query, locale, limit });
const legacyResults = results.map((emoji) =>
'shortcode' in emoji
? ({ id: emoji.shortcode, custom: true } as const)
: {
id: emoji.label.replaceAll(' ', '_').toLowerCase(),
native: emoji.unicode,
},
);
searchCache.set(cacheKey, legacyResults);
return legacyResults;
} }
const defaultCategories = [ const defaultCategories = [

View File

@ -56,13 +56,18 @@ export async function search({
query: rawQuery, query: rawQuery,
locale: localeString, locale: localeString,
limit = 0, limit = 0,
signal,
}: { }: {
query: string; query: string;
locale: string; locale: string;
limit?: number; limit?: number;
signal?: AbortSignal;
}) { }) {
log('searching for "%s"', rawQuery);
performance.mark('emoji-search-start'); performance.mark('emoji-search-start');
signal?.throwIfAborted();
// Get the locale, and extract tokens from the query. // Get the locale, and extract tokens from the query.
const locale = toSupportedLocale(localeString); const locale = toSupportedLocale(localeString);
const segmenter = localeToSegmenter(locale); const segmenter = localeToSegmenter(locale);
@ -93,6 +98,7 @@ export async function search({
locale, locale,
i === queryTokens.length - 1, i === queryTokens.length - 1,
); );
signal?.throwIfAborted();
const resultMap: ScoreMap = new Map(); const resultMap: ScoreMap = new Map();
const checkedSet = new Set<string>(); const checkedSet = new Set<string>();
@ -121,6 +127,7 @@ export async function search({
} }
// Score based on legacy shortcodes, using the higher score if there's a match. // Score based on legacy shortcodes, using the higher score if there's a match.
signal?.throwIfAborted();
for (const shortcodeResult of shortcodeResults) { for (const shortcodeResult of shortcodeResults) {
const emoji = const emoji =
resultMap.get(shortcodeResult.hexcode) ?? resultMap.get(shortcodeResult.hexcode) ??
@ -183,7 +190,9 @@ export async function search({
// If there are no results, try a cursor-based custom emoji search instead. // If there are no results, try a cursor-based custom emoji search instead.
if (mixedResults.length === 0 || mixedResults.length < limit) { if (mixedResults.length === 0 || mixedResults.length < limit) {
signal?.throwIfAborted();
const customEmojisFound = await fullCustomSearch(query, allEmojiIds); const customEmojisFound = await fullCustomSearch(query, allEmojiIds);
signal?.throwIfAborted();
if (customEmojisFound.length > 0) { if (customEmojisFound.length > 0) {
log( log(
'cursor search found %d results for "%s"', 'cursor search found %d results for "%s"',
@ -373,6 +382,8 @@ async function fullCustomSearch(query: string, existing = new Set<string>()) {
// First iterate over chunks of 1,000 custom emoji keys and find any matches. // First iterate over chunks of 1,000 custom emoji keys and find any matches.
const chunkSize = 1_000; const chunkSize = 1_000;
const maxIterations = 10;
let index = 0;
let lastKey: string | null = null; let lastKey: string | null = null;
let keys: string[] = []; let keys: string[] = [];
do { do {
@ -389,6 +400,10 @@ async function fullCustomSearch(query: string, existing = new Set<string>()) {
foundEmojis.add(key); foundEmojis.add(key);
} }
} }
index++;
if (index >= maxIterations) {
break;
}
} while (keys.length === chunkSize); } while (keys.length === chunkSize);
// Next get the full emojis for all matches. // Next get the full emojis for all matches.

View File

@ -518,7 +518,7 @@ body > [data-popper-placement] {
} }
.autosuggest-account .account__avatar, .autosuggest-account .account__avatar,
.autosuggest-emoji img { .autosuggest-emoji .emojione {
display: block; display: block;
width: 24px; width: 24px;
height: 24px; height: 24px;