From 1f0fb21039628f5a67b892a102b3a69e7cdfbab4 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 29 Apr 2026 21:44:42 +0200 Subject: [PATCH] [Glitch] Fixes custom emoji not appearing in autocomplete Port a47ed3104718ce6f69bca2c028b267988a28c26b to glitch-soc Co-authored-by: Copilot Signed-off-by: Claire --- .../flavours/glitch/actions/compose.js | 16 ++-- .../glitch/features/emoji/emoji_picker.tsx | 82 ++---------------- .../flavours/glitch/features/emoji/picker.ts | 85 +++++++++++++++++++ 3 files changed, 101 insertions(+), 82 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/emoji/picker.ts diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 7e4b48a857..464ba6a0c2 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -8,6 +8,7 @@ import { browserHistory } from 'flavours/glitch/components/router'; import { countableText } from 'flavours/glitch/features/compose/util/counter'; import { search as emojiSearch } from 'flavours/glitch/features/emoji/emoji_mart_search_light'; import { tagHistory } from 'flavours/glitch/settings'; +import { fetchCustomEmojiData } from '@/flavours/glitch/features/emoji/picker'; import { recoverHashtags } from 'flavours/glitch/utils/hashtag'; import { showAlert, showAlertForError } from './alerts'; @@ -597,7 +598,7 @@ export function clearComposeSuggestions() { }; } -const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => { +const fetchComposeSuggestionsAccounts = throttle((dispatch, token) => { if (fetchComposeSuggestionsAccountsController) { fetchComposeSuggestionsAccountsController.abort(); } @@ -624,12 +625,13 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => }); }, 200, { leading: true, trailing: true }); -const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => { - const results = emojiSearch(token.replace(':', ''), { maxResults: 5 }); +const fetchComposeSuggestionsEmojis = async (dispatch, token) => { + const custom = await fetchCustomEmojiData(); + const results = emojiSearch(token.replace(':', ''), { maxResults: 5, custom }); dispatch(readyComposeSuggestionsEmojis(token, results)); }; -const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => { +const fetchComposeSuggestionsTags = throttle((dispatch, token) => { if (fetchComposeSuggestionsTagsController) { fetchComposeSuggestionsTagsController.abort(); } @@ -662,14 +664,14 @@ export function fetchComposeSuggestions(token) { return (dispatch, getState) => { switch (token[0]) { case ':': - fetchComposeSuggestionsEmojis(dispatch, getState, token); + void fetchComposeSuggestionsEmojis(dispatch, token); break; case '#': case '#': - fetchComposeSuggestionsTags(dispatch, getState, token); + fetchComposeSuggestionsTags(dispatch, token); break; default: - fetchComposeSuggestionsAccounts(dispatch, getState, token); + fetchComposeSuggestionsAccounts(dispatch, token); break; } }; diff --git a/app/javascript/flavours/glitch/features/emoji/emoji_picker.tsx b/app/javascript/flavours/glitch/features/emoji/emoji_picker.tsx index 0de17cf506..0525412d99 100644 --- a/app/javascript/flavours/glitch/features/emoji/emoji_picker.tsx +++ b/app/javascript/flavours/glitch/features/emoji/emoji_picker.tsx @@ -1,39 +1,19 @@ import type { FC } from 'react'; -import { useEffect, useState } from 'react'; -import type { - CategoryName, - CustomEmoji, - EmojiProps, - PickerProps, -} from 'emoji-mart'; +import type { EmojiProps, PickerProps } from 'emoji-mart'; import EmojiRaw from 'emoji-mart/dist-es/components/emoji/nimble-emoji'; import PickerRaw from 'emoji-mart/dist-es/components/picker/nimble-picker'; -import { autoPlayGif } from '@/flavours/glitch/initial_state'; -import { assetHost } from 'flavours/glitch/utils/config'; +import { assetHost } from '@/flavours/glitch/utils/config'; import { EMOJI_MODE_NATIVE } from './constants'; import EmojiData from './emoji_data.json'; import { useEmojiAppState } from './mode'; -import { emojiLogger } from './utils'; +import { usePickerEmojis } from './picker'; const backgroundImageFnDefault = () => `${assetHost}/emoji/sheet_16_0.png`; -let customEmojis: CustomEmoji[] | null = null; -let customCategories = [ - 'recent', - 'people', - 'nature', - 'foods', - 'activity', - 'places', - 'objects', - 'symbols', - 'flags', -] as CategoryName[]; - -const log = emojiLogger('picker'); +export { fetchCustomEmojiData as loadCustomEmojiData } from './picker'; export const Picker: FC = ({ set = 'twitter', @@ -44,24 +24,16 @@ export const Picker: FC = ({ ...props }) => { const { mode } = useEmojiAppState(); - const [isLoaded, setLoaded] = useState(customEmojis !== null); + const { customCategories, customEmojis } = usePickerEmojis(); - useEffect(() => { - if (customEmojis === null) { - void loadCustomEmojiData().then(() => { - setLoaded(true); - }); - } - }, []); - - if (!isLoaded) { + if (!customEmojis) { return null; } return ( = ({ ); }; -export async function loadCustomEmojiData() { - const { loadAllCustomEmoji } = await import('./database'); - const emojisRaw = await loadAllCustomEmoji(); - if (emojisRaw.length === 0) { - return; - } - - const categories = new Set(['custom']); - const emojis = []; - for (const emoji of emojisRaw) { - const name = emoji.shortcode.replaceAll(':', ''); - emojis.push({ - name, - id: name, - custom: true, - short_names: [name], - imageUrl: autoPlayGif ? emoji.url : emoji.static_url, - customCategory: emoji.category, - }); - - if (emoji.category) { - categories.add(`custom-${emoji.category}`); - } - } - - customEmojis = emojis.toSorted((a, b) => { - return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); - }); - customCategories = customCategories.toSpliced( - 1, - 0, - ...(Array.from(categories).toSorted() as CategoryName[]), - ); - log( - 'loaded %d custom emojis in %d categories', - customEmojis.length, - categories.size, - ); -} - export const Emoji: FC = ({ set = 'twitter', sheetSize = 32, diff --git a/app/javascript/flavours/glitch/features/emoji/picker.ts b/app/javascript/flavours/glitch/features/emoji/picker.ts new file mode 100644 index 0000000000..2c7a9301b7 --- /dev/null +++ b/app/javascript/flavours/glitch/features/emoji/picker.ts @@ -0,0 +1,85 @@ +import { useEffect, useState } from 'react'; + +import type { CategoryName, CustomEmoji } from 'emoji-mart'; + +import { autoPlayGif } from '@/flavours/glitch/initial_state'; + +import { emojiLogger } from './utils'; + +const log = emojiLogger('picker'); + +let customEmojis: CustomEmoji[] | null = null; +let customCategories = [ + 'recent', + 'people', + 'nature', + 'foods', + 'activity', + 'places', + 'objects', + 'symbols', + 'flags', +] as CategoryName[]; + +export async function fetchCustomEmojiData() { + if (customEmojis !== null) { + return customEmojis; + } + + const { loadAllCustomEmoji } = await import('./database'); + const emojisRaw = await loadAllCustomEmoji(); + if (emojisRaw.length === 0) { + return []; + } + + const categories = new Set(['custom']); + const emojis = []; + for (const emoji of emojisRaw) { + const name = emoji.shortcode.replaceAll(':', ''); + emojis.push({ + name, + id: name, + custom: true, + short_names: [name], + imageUrl: autoPlayGif ? emoji.url : emoji.static_url, + customCategory: emoji.category, + }); + + if (emoji.category) { + categories.add(`custom-${emoji.category}`); + } + } + + customEmojis = emojis.toSorted((a, b) => { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + customCategories = customCategories.toSpliced( + 1, + 0, + ...(Array.from(categories).toSorted() as CategoryName[]), + ); + log( + 'loaded %d custom emojis in %d categories', + customEmojis.length, + categories.size, + ); + + return customEmojis; +} + +export function usePickerEmojis() { + const [, setLoaded] = useState(customEmojis !== null); + + useEffect(() => { + if (customEmojis === null) { + void fetchCustomEmojiData().then(() => { + setLoaded(true); + }); + } + }, []); + + return { + customEmojis, + customCategories, + }; +}