From e19aec5abd3dd8e20f7ea9c0b93117b6e08acf42 Mon Sep 17 00:00:00 2001 From: Anton Alexeyev Date: Thu, 1 May 2025 17:11:15 +0700 Subject: [PATCH] Remove redundant skin tone code Signed-off-by: Anton Alexeyev --- .../src/components/emoji/EmojiButton.svelte | 14 ++- .../ui/src/components/emoji/EmojiPopup.svelte | 25 ++--- .../src/components/emoji/SkinTonePopup.svelte | 8 +- .../components/emoji/SkinToneTooltip.svelte | 10 +- packages/ui/src/components/emoji/store.ts | 15 --- packages/ui/src/components/emoji/types.ts | 8 +- packages/ui/src/components/emoji/utils.ts | 96 ++++++------------- plugins/activity-resources/src/utils.ts | 4 +- .../src/components/extension/emoji.ts | 2 +- 9 files changed, 64 insertions(+), 118 deletions(-) diff --git a/packages/ui/src/components/emoji/EmojiButton.svelte b/packages/ui/src/components/emoji/EmojiButton.svelte index 67eaa8a2bb..5caae69fce 100644 --- a/packages/ui/src/components/emoji/EmojiButton.svelte +++ b/packages/ui/src/components/emoji/EmojiButton.svelte @@ -6,7 +6,6 @@ import { createEventDispatcher } from 'svelte' import { getEmbeddedLabel } from '@hcengineering/platform' import { tooltip, capitalizeFirstLetter, type EmojiWithGroup, type LabelAndProps } from '../../' - import { getEmoji } from '.' import { type Emoji } from 'emojibase' export let emoji: EmojiWithGroup @@ -18,14 +17,13 @@ const dispatch = createEventDispatcher() - const getSkinsCount = (e: Emoji | EmojiWithGroup): number | undefined => { + const getSkinsCount = (e: EmojiWithGroup): number | undefined => { return Array.isArray(e.skins) ? e.skins.length : undefined } let displayedEmoji: Emoji | EmojiWithGroup - $: _emoji = (getSkinsCount(emoji) ?? 0) > 0 ? emoji : getEmoji(emoji.hexcode)?.parent ?? emoji - $: skinIndex = _emoji?.skins?.findIndex((skin) => skin.tone === skinTone) ?? -1 - $: displayedEmoji = skinTone > 0 && Array.isArray(_emoji.skins) && skinIndex > -1 ? _emoji.skins[skinIndex] : _emoji + $: skinIndex = emoji?.skins?.findIndex((skin) => skin.tone === skinTone) ?? -1 + $: displayedEmoji = skinTone > 0 && Array.isArray(emoji.skins) && skinIndex > -1 ? emoji.skins[skinIndex] : emoji {#if emoji} @@ -34,9 +32,9 @@ class="hulyPopupEmoji-button" class:preview class:selected - class:skins={_emoji?.skins !== undefined && _emoji.skins.length === 5} - class:constructor={_emoji?.skins !== undefined && _emoji.skins.length > 5} - data-skins={getSkinsCount(_emoji)} + class:skins={emoji?.skins !== undefined && emoji.skins.length === 5} + class:constructor={emoji?.skins !== undefined && emoji.skins.length > 5} + data-skins={getSkinsCount(emoji)} {disabled} on:touchstart on:contextmenu diff --git a/packages/ui/src/components/emoji/EmojiPopup.svelte b/packages/ui/src/components/emoji/EmojiPopup.svelte index 599f057e68..d39c23f420 100644 --- a/packages/ui/src/components/emoji/EmojiPopup.svelte +++ b/packages/ui/src/components/emoji/EmojiPopup.svelte @@ -4,7 +4,6 @@ // Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0). // import { createEventDispatcher, onMount, onDestroy } from 'svelte' - import type { Emoji } from 'emojibase' import { Scroller, SearchInput, @@ -13,18 +12,16 @@ showPopup, eventToHTMLElement, ButtonBase, - closeTooltip + closeTooltip, getSkinnedEmoji } from '../../' import plugin from '../../plugin' import { searchEmoji, emojiStore, - generateSkinToneEmojis, emojiCategories, getFrequentlyEmojis, addFrequentlyEmojis, removeFrequentlyEmojis, - getEmoji, getSkinTone, setSkinTone } from '.' @@ -100,25 +97,23 @@ if (selectedCategory !== undefined) currentCategory = selectedCategory } - const sendEmoji = (emoji: Emoji | EmojiWithGroup): void => { + const sendEmoji = (emoji: EmojiWithGroup): void => { selected = emoji.emoji - addFrequentlyEmojis(emoji.hexcode) + addFrequentlyEmojis(emoji) dispatch('close', { emoji: emoji.emoji, codes: emoji.hexcode.split('-').map((hc) => parseInt(hc, 16)) }) } - const selectedEmoji = (event: CustomEvent<{ detail: EmojiWithGroup }>): void => { + const selectedEmoji = (event: CustomEvent): void => { if (event.detail === undefined || typeof event.detail !== 'object') return - const detail = event.detail as unknown as EmojiWithGroup - sendEmoji(detail) + sendEmoji(event.detail) } function openContextMenu (event: TouchEvent | MouseEvent, _emoji: EmojiWithGroup, remove: boolean): void { event.preventDefault() - const temp = getEmoji(_emoji.hexcode) - const emoji = temp?.parent ?? temp?.emoji + const emoji = _emoji if (emoji === undefined) return clearTimer() @@ -127,9 +122,9 @@ ActionsPopup, { emoji, remove }, eventToHTMLElement(event), - (result: 'remove' | Emoji | EmojiWithGroup) => { + (result: 'remove' | EmojiWithGroup) => { if (result === 'remove') { - removeFrequentlyEmojis(emoji.hexcode) + removeFrequentlyEmojis(emoji) const index = emojisCat.findIndex((ec) => ec.id === 'frequently-used') if (index > -1) emojisCat[index].emojis = getFrequentlyEmojis() emojisCat = emojisCat.filter( @@ -171,7 +166,7 @@ const showSkinMenu = (event: MouseEvent): void => { shownSTM = true - showPopup(SkinTonePopup, { emoji: 0x1f590, selected: skinTone }, eventToHTMLElement(event), (result) => { + showPopup(SkinTonePopup, { emoji: getSkinnedEmoji(':hand:'), selected: skinTone }, eventToHTMLElement(event), (result) => { if (typeof result === 'number') { skinTone = result setSkinTone(skinTone) @@ -270,7 +265,7 @@ tooltip={{ label: plugin.string.DefaultSkinTone }} on:click={showSkinMenu} > - {generateSkinToneEmojis(0x1f590)[skinTone]} + {getSkinnedEmoji(':hand:', skinTone)?.emoji}
@@ -31,7 +31,7 @@ dispatch('close', index) }} > - {skin} + {skin.emoji} {#if label}{/if} {#if disabled}{/if} diff --git a/packages/ui/src/components/emoji/SkinToneTooltip.svelte b/packages/ui/src/components/emoji/SkinToneTooltip.svelte index d63a49e365..188c8863cd 100644 --- a/packages/ui/src/components/emoji/SkinToneTooltip.svelte +++ b/packages/ui/src/components/emoji/SkinToneTooltip.svelte @@ -4,16 +4,16 @@ // Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0). // import { createEventDispatcher } from 'svelte' - import type { Emoji } from 'emojibase' - import { generateSkinToneEmojis, getEmojiCode, type EmojiWithGroup } from '.' + import { type EmojiWithGroup } from '.' import { ButtonBase, closeTooltip } from '../..' + import { Emoji } from 'emojibase' - export let emoji: number | number[] | string | Emoji | EmojiWithGroup + export let emoji: EmojiWithGroup export let selected: number const dispatch = createEventDispatcher() - const skins: string[] = generateSkinToneEmojis(getEmojiCode(emoji)) + const skins: Emoji[] = emoji.skins ?? []
@@ -30,7 +30,7 @@ closeTooltip() }} > - {skin} + {skin.emoji} {/each}
diff --git a/packages/ui/src/components/emoji/store.ts b/packages/ui/src/components/emoji/store.ts index 3b9366b0f5..5b056fd2d4 100644 --- a/packages/ui/src/components/emoji/store.ts +++ b/packages/ui/src/components/emoji/store.ts @@ -13,11 +13,9 @@ // limitations under the License. // import { writable, derived } from 'svelte/store' -import type { Emoji } from 'emojibase' import type { EmojiWithGroup } from '.' export const emojiStore = writable([]) -export const emojiComponents = writable([]) export const searchEmoji = writable('') export const resultEmojis = derived([emojiStore, searchEmoji], ([emojis, search]) => { @@ -29,16 +27,3 @@ export const resultEmojis = derived([emojiStore, searchEmoji], ([emojis, search] ) : emojis }) - -export const groupsResultEmojis = derived(resultEmojis, (result) => { - const keys = new Set(result.map((res) => res?.key ?? '')) - const groups: string[] = keys?.size > 0 ? [...keys] : [''] - return { - emojis: new Map( - groups.map((group) => { - return [group, result.filter((res) => res.key === group)] - }) - ), - groups - } -}) diff --git a/packages/ui/src/components/emoji/types.ts b/packages/ui/src/components/emoji/types.ts index 5de9a25255..6c1c1a5179 100644 --- a/packages/ui/src/components/emoji/types.ts +++ b/packages/ui/src/components/emoji/types.ts @@ -17,7 +17,13 @@ import type { AnySvelteComponent } from '../..' import type { Emoji } from 'emojibase' export type EmojiWithGroup = Emoji & { key: string } -export type Emojis = Emoji | EmojiWithGroup + +export interface CustomEmoji { + shortcode: string + label: string + url: string + tags?: string[] +} export interface EmojiCategory { id: string diff --git a/packages/ui/src/components/emoji/utils.ts b/packages/ui/src/components/emoji/utils.ts index f996148e69..66de532271 100644 --- a/packages/ui/src/components/emoji/utils.ts +++ b/packages/ui/src/components/emoji/utils.ts @@ -13,15 +13,15 @@ // limitations under the License. // import { get } from 'svelte/store' +import type { Emoji, Locale } from 'emojibase' import { fetchEmojis, fetchMessages } from 'emojibase' import EMOJI_REGEX from 'emojibase-regex' import EMOTICON_REGEX from 'emojibase-regex/emoticon' import SHORTCODE_REGEX from 'emojibase-regex/shortcode' -import type { Emoji, Locale } from 'emojibase' import { getCurrentAccount } from '@hcengineering/core' import { deviceOptionsStore as deviceInfo } from '../..' -import { emojiStore, emojiComponents, emojiCategories, skinTonesCodes } from '.' -import type { EmojiWithGroup, EmojiHierarchy } from '.' +import type { EmojiWithGroup } from '.' +import { emojiCategories, emojiStore } from '.' export const emojiRegex = EMOJI_REGEX export const emojiGlobalRegex = new RegExp(EMOJI_REGEX.source, EMOJI_REGEX.flags + 'g') @@ -32,12 +32,9 @@ export const emoticonGlobalRegex = new RegExp(`(? { +export async function loadEmojis (lang?: string): Promise { const local = lang ?? get(deviceInfo).language ?? 'en' const englishEmojis = local === 'en' @@ -64,27 +61,32 @@ export async function loadEmojis (lang?: string): Promise<{ }) : (englishEmojis as Emoji[]) - return { - emojis: emojis - .filter((e) => e.group !== 2 && e.group !== undefined) - .map((e) => { - return { ...e, key: categories.get(groupKeys.get(e?.group ?? 0) ?? '') ?? '' } - }), - components: emojis.filter((e) => e.group === 2) - } + return emojis + .filter((e) => e.group !== 2 && e.group !== undefined) + .map((e) => { + return { ...e, key: categories.get(groupKeys.get(e?.group ?? 0) ?? '') ?? '' } + }) } export async function updateEmojis (lang?: string): Promise { - const { emojis, components } = await loadEmojis(lang) + const emojis = await loadEmojis(lang) availableEmojis = emojis emojiStore.set(emojis) - emojiComponents.set(components) +} + +export function getSkinnedEmoji (shortcode: string | undefined, skinTone?: number): Emoji | undefined { + if (shortcode === undefined) return undefined + const shortcodeSlice = shortcode.slice(1, -1) + const matchEmoji = availableEmojis.find((e) => e.shortcodes?.includes(shortcodeSlice)) + if (skinTone === undefined || matchEmoji === undefined) return matchEmoji + if (skinTone === 0) return matchEmoji + return matchEmoji.skins === undefined ? undefined : matchEmoji.skins[skinTone - 1] } export function getEmojiForShortCode (shortcode: string | undefined): string | undefined { - shortcode = shortcode?.slice(1, -1) if (shortcode === undefined) return undefined - const result = availableEmojis.find(e => e.shortcodes?.includes(shortcode)) + const shortcodeSlice = shortcode.slice(1, -1) + const result = availableEmojis.find(e => e.shortcodes?.includes(shortcodeSlice)) return result === undefined ? undefined : result.emoji } @@ -99,9 +101,8 @@ function getEmojisLocalStorageKey (suffix: string = 'frequently'): string { return `emojis.${suffix}.${me.uuid}` } -export const removeFrequentlyEmojis = (_hexcode: string): void => { - const emoji = getEmoji(_hexcode) - const hexcode = emoji?.parent?.hexcode ?? emoji?.emoji.hexcode +export const removeFrequentlyEmojis = (emoji: EmojiWithGroup): void => { + const hexcode = emoji.hexcode if (hexcode === undefined) return const frequentlyEmojisKey = getEmojisLocalStorageKey() @@ -116,10 +117,9 @@ export const removeFrequentlyEmojis = (_hexcode: string): void => { } } } -export const addFrequentlyEmojis = (_hexcode: string): void => { - const emoji = getEmoji(_hexcode) - const hexcode = emoji?.parent?.hexcode ?? emoji?.emoji.hexcode - if (hexcode === undefined) return +export const addFrequentlyEmojis = (emoji: EmojiWithGroup): void => { + if (emoji === undefined) return + const hexcode = emoji.hexcode const frequentlyEmojisKey = getEmojisLocalStorageKey() const frequentlyEmojis = window.localStorage.getItem(frequentlyEmojisKey) @@ -146,18 +146,8 @@ export const getFrequentlyEmojis = (): EmojiWithGroup[] | undefined => { try { const parsedEmojis = JSON.parse(frequentlyEmojis) if (!Array.isArray(parsedEmojis)) return undefined - - const res: EmojiWithGroup[] = [] - - for (const val of parsedEmojis) { - const map = getEmoji(val.hexcode) - const emoji = map?.parent ?? map?.emoji - if (emoji !== undefined) { - res.push(emoji) - } - } - - return res + const emojis = get(emojiStore) + return emojis.filter(e => parsedEmojis.find(pe => pe.hexcode === e.hexcode) !== undefined) } catch (e) { console.error(e) return undefined @@ -180,31 +170,3 @@ export const getSkinTone = (): number => { return 0 } } -export const generateSkinToneEmojis = (baseEmoji: number | number[]): string[] => { - const isArray = Array.isArray(baseEmoji) - return [ - String.fromCodePoint(...(isArray ? baseEmoji : [baseEmoji])), - ...skinTonesCodes.map((skinTone) => { - return String.fromCodePoint(...(isArray ? baseEmoji : [baseEmoji]), skinTone) - }) - ] -} - -export const getEmojiMap = (): Map | undefined => { - const result = new Map() - const emojis = get(emojiStore) - - emojis.forEach((emoji) => { - result.set(emoji.hexcode, { emoji }) - emoji.skins?.forEach((skin) => result.set(skin.hexcode, { emoji: { ...skin, key: emoji.key }, parent: emoji })) - }) - return result -} -export const getEmoji = (hexcode: string): EmojiHierarchy | undefined => { - return getEmojiMap()?.get(hexcode) -} -export const getEmojiCode = (e: number | number[] | string | Emoji | EmojiWithGroup): number | number[] => { - return typeof e === 'number' || Array.isArray(e) - ? e - : (typeof e === 'object' ? e.hexcode : e).split('-').map((h) => parseInt(h, 16)) -} diff --git a/plugins/activity-resources/src/utils.ts b/plugins/activity-resources/src/utils.ts index 19895ca0c5..188f3a26d5 100644 --- a/plugins/activity-resources/src/utils.ts +++ b/plugins/activity-resources/src/utils.ts @@ -8,7 +8,7 @@ import { getEventPositionElement, showPopup, type Location, - type Emojis + type EmojiWithGroup } from '@hcengineering/ui' import { type AttributeModel } from '@hcengineering/view' import { get } from 'svelte/store' @@ -61,7 +61,7 @@ export async function addReactionAction ( closePopup() - showPopup(EmojiPopup, {}, element, (emoji: Emojis) => { + showPopup(EmojiPopup, {}, element, (emoji: EmojiWithGroup) => { if (emoji?.emoji !== undefined) void updateDocReactions(reactions, message, emoji.emoji) params?.onClose?.() }) diff --git a/plugins/text-editor-resources/src/components/extension/emoji.ts b/plugins/text-editor-resources/src/components/extension/emoji.ts index d7ebfe33e7..43efb170ae 100644 --- a/plugins/text-editor-resources/src/components/extension/emoji.ts +++ b/plugins/text-editor-resources/src/components/extension/emoji.ts @@ -34,7 +34,7 @@ function handleEmoji ( if (!isValidEmojiPosition($from)) { return } - const emoji = getEmojiFunction(match[0]) + const emoji = getEmojiFunction(match.pop()) if (emoji === undefined) return commands.insertContentAt(range, [ {