diff --git a/.changeset/fix-emoji-type-standardization.md b/.changeset/fix-emoji-type-standardization.md new file mode 100644 index 0000000000000..912e536b3d933 --- /dev/null +++ b/.changeset/fix-emoji-type-standardization.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/core-typings': patch +'@rocket.chat/meteor': patch +--- + +Fixes the `IEmoji` type to reflect the real shape of custom emoji data, and standardizes the `emoji.list` entry types (native, custom, and alias) into a shared `IEmojiPackEntry` type, removing unsafe type casts. diff --git a/apps/meteor/app/emoji/lib/rocketchat.ts b/apps/meteor/app/emoji/lib/rocketchat.ts index a72da9a1e88c6..3478442e9e84b 100644 --- a/apps/meteor/app/emoji/lib/rocketchat.ts +++ b/apps/meteor/app/emoji/lib/rocketchat.ts @@ -1,3 +1,4 @@ +import type { IEmojiPackEntry } from '@rocket.chat/core-typings'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; export type EmojiPackage = { @@ -19,30 +20,6 @@ export type EmojiPackages = { [key: string]: EmojiPackage; }; list: { - [key: keyof NonNullable]: - | { - name?: string; - category: string; - emojiPackage: string; - shortnames: string[]; - uc_base: string; - uc_greedy: string; - uc_match: string; - uc_output: string; - aliases?: string[]; - aliasOf?: undefined; - extension?: string; - etag?: string; - unicode?: string; - } - | { - name?: undefined; - emojiPackage: string; - aliasOf: string; - extension?: undefined; - aliases?: undefined; - shortnames?: undefined; - etag?: string; - }; + [key: keyof NonNullable]: IEmojiPackEntry; }; }; diff --git a/apps/meteor/client/lib/customEmoji.ts b/apps/meteor/client/lib/customEmoji.ts index 777d1e16de916..004f58f5b3303 100644 --- a/apps/meteor/client/lib/customEmoji.ts +++ b/apps/meteor/client/lib/customEmoji.ts @@ -4,22 +4,11 @@ import { escapeRegExp } from '@rocket.chat/tools'; import { emoji, removeFromRecent, replaceEmojiInRecent } from '../../app/emoji/client'; import { getURL } from '../../app/utils/client'; -const isSetNotNull = (fn: () => unknown) => { - let value; - try { - value = fn(); - } catch (e) { - value = null; - } - return value !== null && value !== undefined; -}; - export const updateEmojiCustom = (emojiData: IEmoji) => { - const previousExists = isSetNotNull(() => emojiData.previousName); - const currentAliases = isSetNotNull(() => emojiData.aliases); + const { previousName, aliases = [] } = emojiData; - if (previousExists && isSetNotNull(() => emoji.list[`:${emojiData.previousName}:`].aliases)) { - for (const alias of emoji.list[`:${emojiData.previousName}:`].aliases ?? []) { + if (previousName && emoji.list[`:${previousName}:`]?.aliases) { + for (const alias of emoji.list[`:${previousName}:`].aliases ?? []) { delete emoji.list[`:${alias}:`]; const aliasIndex = emoji.packages.emojiCustom.list?.indexOf(`:${alias}:`) ?? -1; if (aliasIndex !== -1) { @@ -28,41 +17,43 @@ export const updateEmojiCustom = (emojiData: IEmoji) => { } } - if (previousExists && emojiData.name !== emojiData.previousName) { - const arrayIndex = emoji.packages.emojiCustom.emojisByCategory.rocket.indexOf(emojiData.previousName); + if (previousName && emojiData.name !== previousName) { + const arrayIndex = emoji.packages.emojiCustom.emojisByCategory.rocket.indexOf(previousName); if (arrayIndex !== -1) { emoji.packages.emojiCustom.emojisByCategory.rocket.splice(arrayIndex, 1); } - const arrayIndexList = emoji.packages.emojiCustom.list?.indexOf(`:${emojiData.previousName}:`) ?? -1; + const arrayIndexList = emoji.packages.emojiCustom.list?.indexOf(`:${previousName}:`) ?? -1; if (arrayIndexList !== -1) { emoji.packages.emojiCustom.list?.splice(arrayIndexList, 1); } - delete emoji.list[`:${emojiData.previousName}:`]; + delete emoji.list[`:${previousName}:`]; } - const categoryIndex = emoji.packages.emojiCustom.emojisByCategory.rocket.indexOf(`${emojiData.name}`); + const categoryIndex = emoji.packages.emojiCustom.emojisByCategory.rocket.indexOf(emojiData.name); if (categoryIndex === -1) { - emoji.packages.emojiCustom.emojisByCategory.rocket.push(`${emojiData.name}`); + emoji.packages.emojiCustom.emojisByCategory.rocket.push(emojiData.name); emoji.packages.emojiCustom.list?.push(`:${emojiData.name}:`); } - // Don't inherit fields from a native emoji being overridden (e.g. its unicode), or the pick would output the native emoji - // TODO: Fix the IEmoji type and standardize the emoji packs types + + // Don't inherit fields from a native emoji being overridden (e.g. its unicode), or the picker would output the native emoji emoji.list[`:${emojiData.name}:`] = { - ...emojiData, + name: emojiData.name, + extension: emojiData.extension, + etag: emojiData.etag, + aliases, emojiPackage: 'emojiCustom', - } as unknown as (typeof emoji.list)[keyof typeof emoji.list]; - if (currentAliases) { - for (const alias of emojiData.aliases) { - emoji.packages.emojiCustom.list?.push(`:${alias}:`); - emoji.list[`:${alias}:`] = { - emojiPackage: 'emojiCustom', - aliasOf: emojiData.name, - }; - } + }; + + for (const alias of aliases) { + emoji.packages.emojiCustom.list?.push(`:${alias}:`); + emoji.list[`:${alias}:`] = { + emojiPackage: 'emojiCustom', + aliasOf: emojiData.name, + }; } - if (previousExists) { - replaceEmojiInRecent({ oldEmoji: emojiData.previousName, newEmoji: emojiData.name }); + if (previousName) { + replaceEmojiInRecent({ oldEmoji: previousName, newEmoji: emojiData.name }); } emoji.dispatchUpdate(); diff --git a/apps/meteor/client/views/root/hooks/loggedIn/useCustomEmoji.ts b/apps/meteor/client/views/root/hooks/loggedIn/useCustomEmoji.ts index 297e7c8bf6619..353a9fa88e4c1 100644 --- a/apps/meteor/client/views/root/hooks/loggedIn/useCustomEmoji.ts +++ b/apps/meteor/client/views/root/hooks/loggedIn/useCustomEmoji.ts @@ -38,7 +38,13 @@ export const useCustomEmoji = () => { for (const currentEmoji of customEmojis) { emoji.packages.emojiCustom.emojisByCategory.rocket.push(currentEmoji.name); emoji.packages.emojiCustom.list?.push(`:${currentEmoji.name}:`); - emoji.list[`:${currentEmoji.name}:`] = { ...currentEmoji, emojiPackage: 'emojiCustom' } as any; + emoji.list[`:${currentEmoji.name}:`] = { + name: currentEmoji.name, + extension: currentEmoji.extension, + etag: currentEmoji.etag, + aliases: currentEmoji.aliases, + emojiPackage: 'emojiCustom', + }; for (const alias of currentEmoji.aliases) { emoji.packages.emojiCustom.list?.push(`:${alias}:`); emoji.list[`:${alias}:`] = { diff --git a/packages/core-typings/src/IEmoji.ts b/packages/core-typings/src/IEmoji.ts index af9cb8d8fc701..0034d2f77faa6 100644 --- a/packages/core-typings/src/IEmoji.ts +++ b/packages/core-typings/src/IEmoji.ts @@ -1,3 +1,14 @@ +// Shape of the data broadcast for a custom emoji create/update/delete (see `emoji.updateCustom` / +// `emoji.deleteCustom` events and the `updateEmojiCustom` / `deleteEmojiCustom` notification streams). +// It is a superset of the fields sent from the different call sites that publish these events, so most +// fields besides `name` and `extension` are optional. export interface IEmoji { - [x: string]: any; + _id?: string; + name: string; + aliases?: string[]; + extension: string; + etag?: string; + previousName?: string; + previousExtension?: string; + newFile?: boolean; } diff --git a/packages/core-typings/src/IEmojiPackEntry.ts b/packages/core-typings/src/IEmojiPackEntry.ts new file mode 100644 index 0000000000000..22793311d6e41 --- /dev/null +++ b/packages/core-typings/src/IEmojiPackEntry.ts @@ -0,0 +1,39 @@ +// An entry of `emoji.list`, i.e. a single renderable emoji or emoji alias registered by an emoji pack +// (native/unicode, custom, or any package added through the emoji Apps-Engine hooks). +export type INativeEmojiPackEntry = { + name?: string; + category: string; + emojiPackage: string; + shortnames: string[]; + uc_base: string; + uc_greedy: string; + uc_match: string; + uc_output: string; + aliases?: string[]; + aliasOf?: undefined; + extension?: string; + etag?: string; + unicode?: string; +}; + +export type ICustomEmojiPackEntry = { + name: string; + emojiPackage: string; + extension: string; + aliases?: string[]; + aliasOf?: undefined; + shortnames?: undefined; + etag?: string; +}; + +export type IEmojiAliasPackEntry = { + name?: undefined; + emojiPackage: string; + aliasOf: string; + extension?: undefined; + aliases?: undefined; + shortnames?: undefined; + etag?: string; +}; + +export type IEmojiPackEntry = INativeEmojiPackEntry | ICustomEmojiPackEntry | IEmojiAliasPackEntry; diff --git a/packages/core-typings/src/index.ts b/packages/core-typings/src/index.ts index f44681ebc9f60..833e4c942e540 100644 --- a/packages/core-typings/src/index.ts +++ b/packages/core-typings/src/index.ts @@ -50,6 +50,7 @@ export * from './INps'; export type * from './ISession'; export type * from './IEmoji'; +export type * from './IEmojiPackEntry'; export type * from './IEmojiCustom'; export type * from './ICustomEmojiDescriptor'; export type * from './IAnalytics';