diff --git a/packages/nicolium/package.json b/packages/nicolium/package.json index 18d9b749d..902e7d46c 100644 --- a/packages/nicolium/package.json +++ b/packages/nicolium/package.json @@ -87,7 +87,6 @@ "exifr": "^7.1.3", "fast-average-color": "^9.5.0", "fasttext.wasm.js": "^1.0.0", - "flexsearch": "^0.7.43", "fuzzysort": "^3.1.0", "graphemesplit": "^2.6.0", "html-react-parser": "^5.2.17", diff --git a/packages/nicolium/src/features/chats/components/chat-composer.tsx b/packages/nicolium/src/features/chats/components/chat-composer.tsx index e2f2d0b37..ea241edab 100644 --- a/packages/nicolium/src/features/chats/components/chat-composer.tsx +++ b/packages/nicolium/src/features/chats/components/chat-composer.tsx @@ -20,6 +20,7 @@ import { useRelationshipQuery, useUnblockAccountMutation, } from '@/queries/accounts/use-relationship'; +import { useCustomEmojis } from '@/queries/instance/use-custom-emojis'; import { useModalsActions } from '@/stores/modals'; import { textAtCursorMatchesToken } from '@/utils/suggestions'; @@ -95,6 +96,7 @@ const ChatComposer = React.forwardRef const { chat } = useChatContext(); const { data: relationship } = useRelationshipQuery(chat?.account.id); const { mutate: unblockAccount } = useUnblockAccountMutation(chat?.account.id!); + const { data: customEmojis } = useCustomEmojis(); const isBlocked = relationship?.blocked_by && false; const isBlocking = relationship?.blocking && false; @@ -130,7 +132,7 @@ const ChatComposer = React.forwardRef ); if (token && tokenStart) { - const results = emojiSearch(token.replace(':', ''), { maxResults: 5 }); + const results = emojiSearch(token.replace(':', ''), customEmojis, 5); setSuggestions({ list: results, token, diff --git a/packages/nicolium/src/features/emoji/search.ts b/packages/nicolium/src/features/emoji/search.ts index 7da79869a..e8c0e1154 100644 --- a/packages/nicolium/src/features/emoji/search.ts +++ b/packages/nicolium/src/features/emoji/search.ts @@ -1,4 +1,4 @@ -import FlexSearch from 'flexsearch'; +import fuzzysort from 'fuzzysort'; import type { EmojiData } from './data'; import type { Emoji } from './index'; @@ -6,79 +6,57 @@ import type { CustomEmoji } from 'pl-api'; let emojis: EmojiData['emojis'] = {}; +const nativeData: Array<{ key: string; id: string }> = []; +let customData: Array<{ key: string; id: string }> = []; + import('./data') .then((data) => { emojis = data.emojis; const sortedEmojis = Object.entries(emojis).toSorted((a, b) => a[0].localeCompare(b[0])); for (const [key, emoji] of sortedEmojis) { - index.add('n' + key, `${emoji.id} ${emoji.name} ${emoji.keywords.join(' ')}`); + nativeData.push({ + key: `${emoji.id} ${emoji.name} ${emoji.keywords.join(' ')}`, + id: 'n' + key, + }); } }) .catch(() => {}); -const index = new FlexSearch.Index({ - tokenize: 'full', - optimize: true, - context: true, -}); - -interface searchOptions { - maxResults?: number; - custom?: CustomEmoji[]; -} - const addCustomToPool = (customEmojis: CustomEmoji[]) => { - // @ts-expect-error - for (const key in index.register) { - if (key[0] === 'c') { - index.remove(key); // remove old custom emojis - } - } - - let i = 0; - - for (const emoji of customEmojis) { - index.add('c' + i++, emoji.shortcode); - } + customData = customEmojis.map((emoji, i) => ({ + key: emoji.shortcode, + id: 'c' + i, + })); }; -// we can share an index by prefixing custom emojis with 'c' and native with 'n' -const search = ( - str: string, - { maxResults = 5 }: searchOptions = {}, - custom_emojis?: Array, -): Emoji[] => - index - .search(str, maxResults) - .flatMap((id) => { - if (typeof id !== 'string') return; +const search = (query: string, customEmojis: Array = [], limit = 5): Emoji[] => { + return fuzzysort + .go(query, [...nativeData, ...customData], { key: 'key', limit }) + .map((result) => { + const { id } = result.obj; - if (id[0] === 'c' && custom_emojis) { - const index = Number(id.slice(1)); - const custom = custom_emojis[index]; - - if (custom) { - return { - id: custom.shortcode, - colons: ':' + custom.shortcode + ':', - custom: true, - imageUrl: custom.static_url, - }; - } + if (id[0] === 'c') { + const customEmoji = customEmojis[Number(id.slice(1))]; + return { + id: customEmoji.shortcode, + colons: ':' + customEmoji.shortcode + ':', + custom: true, + imageUrl: customEmoji.static_url, + }; } - const skins = emojis[id.slice(1)]?.skins; - - if (skins) { + const emojiData = emojis[id.slice(1)]; + if (emojiData) { return { id: id.slice(1), colons: ':' + id.slice(1) + ':', - unified: skins[0].unified, - native: skins[0].native, + unified: emojiData.skins[0].unified, + native: emojiData.skins[0].native, }; } }) - .filter(Boolean) as Emoji[]; + .filter(Boolean) as Array; +}; export { search as default, addCustomToPool }; diff --git a/packages/nicolium/src/hooks/use-compose-suggestions.ts b/packages/nicolium/src/hooks/use-compose-suggestions.ts index bc92fdf36..a47339c99 100644 --- a/packages/nicolium/src/hooks/use-compose-suggestions.ts +++ b/packages/nicolium/src/hooks/use-compose-suggestions.ts @@ -32,7 +32,7 @@ const useComposeSuggestions = (token: string): Array => { return useMemo((): Array => { if (searchedType === 'emojis') { - return emojiSearch(token.replace(':', ''), { maxResults: 10 }, customEmojis); + return emojiSearch(token.replace(':', ''), customEmojis, 10); } if (searchedType === 'accounts') { diff --git a/packages/nicolium/src/styles/new/layout.scss b/packages/nicolium/src/styles/new/layout.scss index 4230849da..feeb634c7 100644 --- a/packages/nicolium/src/styles/new/layout.scss +++ b/packages/nicolium/src/styles/new/layout.scss @@ -621,4 +621,4 @@ div:has(.⁂-background-shapes), &--error svg { @apply text-danger-600; } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2dc41d59f..808b03278 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,9 +178,6 @@ importers: fasttext.wasm.js: specifier: ^1.0.0 version: 1.0.0 - flexsearch: - specifier: ^0.7.43 - version: 0.7.43 fuzzysort: specifier: ^3.1.0 version: 3.1.0 @@ -3859,9 +3856,6 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - flexsearch@0.7.43: - resolution: {integrity: sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==} - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -10096,8 +10090,6 @@ snapshots: flatted@3.3.3: {} - flexsearch@0.7.43: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7