From 635d1e36b2e5434ad5993fa0a9cf923aa16aa1d7 Mon Sep 17 00:00:00 2001 From: mkljczk Date: Thu, 6 Mar 2025 13:20:25 +0100 Subject: [PATCH] pl-fe: Support custom emoji categories Signed-off-by: mkljczk --- .../components/emoji-picker-dropdown.tsx | 10 +++-- packages/pl-fe/src/features/emoji/index.ts | 38 ++++++++++++++++++- packages/pl-fe/src/locales/en.json | 1 + 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/pl-fe/src/features/emoji/components/emoji-picker-dropdown.tsx b/packages/pl-fe/src/features/emoji/components/emoji-picker-dropdown.tsx index 49cd2984f..4d87b073a 100644 --- a/packages/pl-fe/src/features/emoji/components/emoji-picker-dropdown.tsx +++ b/packages/pl-fe/src/features/emoji/components/emoji-picker-dropdown.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useLayoutEffect, Suspense, useMemo } from 'react'; +import React, { useEffect, useLayoutEffect, useMemo, useState, Suspense } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { changeSetting, saveSettings } from 'pl-fe/actions/settings'; @@ -8,7 +8,7 @@ import { useTheme } from 'pl-fe/hooks/use-theme'; import { useCustomEmojis } from 'pl-fe/queries/instance/use-custom-emojis'; import { useSettingsStore } from 'pl-fe/stores/settings'; -import { buildCustomEmojis } from '../../emoji'; +import { buildCustomEmojiCategories } from '../../emoji'; import { EmojiPicker } from '../../ui/util/async-components'; import type { CustomEmoji as BaseCustomEmoji } from 'pl-api'; @@ -209,12 +209,16 @@ const EmojiPickerDropdown: React.FC = ({ document.body.style.overflow = ''; }, []); + const customEmojiCategories = useMemo(() => { + return withCustom ? buildCustomEmojiCategories(customEmojis || [], intl) : undefined; + }, [withCustom, customEmojis]); + return ( visible ? ( const buildCustomEmojis = (customEmojis: Array) => { const emojis: EmojiMart[] = []; - customEmojis.forEach((emoji: any) => { + customEmojis.forEach((emoji) => { const shortcode = emoji.shortcode; const url = emoji.static_url; const name = shortcode.replace(':', ''); @@ -72,6 +74,39 @@ const buildCustomEmojis = (customEmojis: Array) => { return emojis; }; +const buildCustomEmojiCategories = (customEmojis: Array, intl?: IntlShape) => { + const emojiCategories: Record[]> = {}; + + for (const emoji of customEmojis) { + const categoryName = emoji.category || 'uncategorized'; + if (!emojiCategories[categoryName]) { + emojiCategories[categoryName] = []; + } + const category = emojiCategories[categoryName]; + + const shortcode = emoji.shortcode; + const url = emoji.static_url; + const name = shortcode.replace(':', ''); + + category.push({ + id: name, + name, + keywords: [name], + skins: [{ src: url }], + }); + } + + return Object.entries(emojiCategories) + .toSorted((a, b) => a[0].localeCompare(b[0])) + .map(([categoryName, emojis]) => ({ + id: categoryName, + name: categoryName === 'uncategorized' && intl + ? intl.formatMessage({ id: 'emoji_button.uncategorized', defaultMessage: 'Uncategorized' }) + : categoryName.replace(/^pack:/, ''), + emojis, + })); +}; + export { type CustomEmoji, type NativeEmoji, @@ -79,5 +114,6 @@ export { isCustomEmoji, isNativeEmoji, buildCustomEmojis, + buildCustomEmojiCategories, validEmojiChar, }; diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index ca175525d..ffbb06b4f 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -691,6 +691,7 @@ "emoji_button.skins_choose": "Choose default skin tone", "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", + "emoji_button.uncategorized": "Uncategorized", "empty_column.account_blocked": "You are blocked by @{accountUsername}.", "empty_column.account_favourited_statuses": "This user doesn't have any liked posts yet.", "empty_column.account_timeline": "No posts here!",