diff --git a/apps/meteor/app/emoji-emojione/client/lib.ts b/apps/meteor/app/emoji-emojione/client/lib.ts index 04671a6db6d80..408b053c20f8f 100644 --- a/apps/meteor/app/emoji-emojione/client/lib.ts +++ b/apps/meteor/app/emoji-emojione/client/lib.ts @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/client'; -import { getEmojiConfig, isSetNotNull } from '../lib/rocketchat'; import { getUserPreference } from '../../utils/client'; +import { isSetNotNull } from '../lib/isSetNotNull'; +import { getEmojiConfig } from '../lib/getEmojiConfig'; const config = getEmojiConfig(); @@ -18,16 +19,11 @@ if (emoji.packages.emojione) { // RocketChat.emoji.list is the collection of emojis from all emoji packages for (const [key, currentEmoji] of Object.entries(config.emojione.emojioneList)) { - // @ts-expect-error - emojione types currentEmoji.emojiPackage = 'emojione'; - // @ts-expect-error - emojione types emoji.list[key] = currentEmoji; - // @ts-expect-error - emojione types if (currentEmoji.shortnames) { - // @ts-expect-error - emojione types currentEmoji.shortnames.forEach((shortname: string) => { - // @ts-expect-error - emojione types emoji.list[shortname] = currentEmoji; }); } diff --git a/apps/meteor/app/emoji-emojione/lib/emojioneRender.ts b/apps/meteor/app/emoji-emojione/lib/emojioneRender.ts deleted file mode 100644 index 2ed772ce644c3..0000000000000 --- a/apps/meteor/app/emoji-emojione/lib/emojioneRender.ts +++ /dev/null @@ -1,9 +0,0 @@ -import emojione from 'emojione'; - -export function emojioneRender(message: string): string { - return emojione.toImage(message); -} - -export function emojioneRenderFromShort(message: string): string { - return emojione.shortnameToImage(message); -} diff --git a/apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts b/apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts new file mode 100644 index 0000000000000..51d2fbc1c4efc --- /dev/null +++ b/apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts @@ -0,0 +1,281 @@ +import emojione from 'emojione'; +import mem from 'mem'; + +import { emojisByCategory, emojiCategories, toneList } from './emojiPicker'; + +// TODO remove fix below when issue is solved: https://github.com/joypixels/emojione/issues/617 + +// add missing emojis not provided by JS object, but included on emoji.json +emojione.shortnames += + '|:tm:|:copyright:|:registered:|:digit_zero:|:digit_one:|:digit_two:|:digit_three:|:digit_four:|:digit_five:|:digit_six:|:digit_seven:|:digit_eight:|:digit_nine:|:pound_symbol:|:asterisk_symbol:'; +emojione.regShortNames = new RegExp( + `]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(${emojione.shortnames})`, + 'gi', +); + +emojione.emojioneList[':tm:'] = { + uc_base: '2122', + uc_output: '2122-fe0f', + uc_match: '2122-fe0f', + uc_greedy: '2122-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':copyright:'] = { + uc_base: '00a9', + uc_output: '00a9-f0ef', + uc_match: '00a9-fe0f', + uc_greedy: '00a9-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':registered:'] = { + uc_base: '00ae', + uc_output: '00ae-fe0f', + uc_match: '00ae-fe0f', + uc_greedy: '00ae-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_zero:'] = { + uc_base: '0030', + uc_output: '0030-fe0f', + uc_match: '0030-fe0f', + uc_greedy: '0030-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_one:'] = { + uc_base: '0031', + uc_output: '0031-fe0f', + uc_match: '0031-fe0f', + uc_greedy: '0031-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_two:'] = { + uc_base: '0032', + uc_output: '0032-fe0f', + uc_match: '0032-fe0f', + uc_greedy: '0032-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_three:'] = { + uc_base: '0033', + uc_output: '0033-fe0f', + uc_match: '0033-fe0f', + uc_greedy: '0033-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_four:'] = { + uc_base: '0034', + uc_output: '0034-fe0f', + uc_match: '0034-fe0f', + uc_greedy: '0034-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_five:'] = { + uc_base: '0035', + uc_output: '0035-fe0f', + uc_match: '0035-fe0f', + uc_greedy: '0035-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_six:'] = { + uc_base: '0036', + uc_output: '0036-fe0f', + uc_match: '0036-fe0f', + uc_greedy: '0036-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_seven:'] = { + uc_base: '0037', + uc_output: '0037-fe0f', + uc_match: '0037-fe0f', + uc_greedy: '0037-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_eight:'] = { + uc_base: '0038', + uc_output: '0038-fe0f', + uc_match: '0038-fe0f', + uc_greedy: '0038-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_nine:'] = { + uc_base: '0039', + uc_output: '0039-fe0f', + uc_match: '0039-fe0f', + uc_greedy: '0039-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':pound_symbol:'] = { + uc_base: '0023', + uc_output: '0023-fe0f', + uc_match: '0023-fe0f', + uc_greedy: '0023-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':asterisk_symbol:'] = { + uc_base: '002a', + uc_output: '002a-fe0f', + uc_match: '002a-fe0f', + uc_greedy: '002a-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; +// end fix + +// fix for :+1: - had to replace all function that does its conversion: https://github.com/joypixels/emojione/blob/4.5.0/lib/js/emojione.js#L249 + +emojione.shortnameConversionMap = mem(emojione.shortnameConversionMap, { maxAge: 1000 }); + +emojione.unicodeCharRegex = mem(emojione.unicodeCharRegex, { maxAge: 1000 }); + +const convertShortName = mem( + (shortname) => { + // the fix is basically adding this .replace(/[+]/g, '\\$&') + if (typeof shortname === 'undefined' || shortname === '' || emojione.shortnames.indexOf(shortname.replace(/[+]/g, '\\$&')) === -1) { + // if the shortname doesnt exist just return the entire match + return shortname; + } + + // map shortname to parent + if (!emojione.emojioneList[shortname]) { + for (const emoji in emojione.emojioneList) { + if (!emojione.emojioneList.hasOwnProperty(emoji) || emoji === '') { + continue; + } + if (emojione.emojioneList[emoji].shortnames.indexOf(shortname) === -1) { + continue; + } + shortname = emoji; + break; + } + } + + const unicode = emojione.emojioneList[shortname].uc_output; + const fname = emojione.emojioneList[shortname].uc_base; + const category = fname.indexOf('-1f3f') >= 0 ? 'diversity' : emojione.emojioneList[shortname].category; + const title = emojione.imageTitleTag ? `title="${shortname}"` : ''; + // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; + // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path + const ePath = + emojione.defaultPathPNG !== emojione.imagePathPNG ? emojione.imagePathPNG : `${emojione.defaultPathPNG + emojione.emojiSize}/`; + + // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname + const alt = emojione.unicodeAlt ? emojione.convert(unicode.toUpperCase()) : shortname; + + if (emojione.sprites) { + return `${alt}`; + } + return `${alt}`; + }, + { maxAge: 1000 }, +); + +const convertUnicode = mem( + (entire, _m1, m2, m3) => { + const mappedUnicode = emojione.mapUnicodeToShort(); + + if (typeof m3 === 'undefined' || m3 === '' || !(emojione.unescapeHTML(m3) in emojione.asciiList)) { + // if the ascii doesnt exist just return the entire match + return entire; + } + + m3 = emojione.unescapeHTML(m3); + const unicode = emojione.asciiList[m3]; + const shortname = mappedUnicode[unicode]; + const category = unicode.indexOf('-1f3f') >= 0 ? 'diversity' : emojione.emojioneList[shortname].category; + const title = emojione.imageTitleTag ? `title="${emojione.escapeHTML(m3)}"` : ''; + // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; + // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path + const ePath = + emojione.defaultPathPNG !== emojione.imagePathPNG ? emojione.imagePathPNG : `${emojione.defaultPathPNG + emojione.emojiSize}/`; + + // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname + const alt = emojione.unicodeAlt ? emojione.convert(unicode.toUpperCase()) : emojione.escapeHTML(m3); + + if (emojione.sprites) { + return `${m2}${alt}`; + } + return `${m2}${alt}`; + }, + { maxAge: 1000, cacheKey: JSON.stringify }, +); + +emojione.shortnameToImage = (str) => { + // replace regular shortnames first + str = str.replace(emojione.regShortNames, convertShortName); + + // if ascii smileys are turned on, then we'll replace them! + if (emojione.ascii) { + const asciiRX = emojione.riskyMatchAscii ? emojione.regAsciiRisky : emojione.regAscii; + + return str.replace(asciiRX, convertUnicode); + } + + return str; +}; + +const isEmojiSupported = (str: string) => { + str = str.replace(emojione.regShortNames, convertShortName); + + // if ascii smileys are turned on, then we'll replace them! + if (emojione.ascii) { + const asciiRX = emojione.riskyMatchAscii ? emojione.regAsciiRisky : emojione.regAscii; + + return str.replace(asciiRX, convertUnicode); + } + + return str; +}; + +export const getEmojiConfig = () => ({ + emojione, + emojisByCategory, + emojiCategories, + toneList, + render: emojione.toImage, + renderPicker: emojione.shortnameToImage, + sprites: true, + isEmojiSupported, +}); diff --git a/apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts b/apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts new file mode 100644 index 0000000000000..000be1279e684 --- /dev/null +++ b/apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts @@ -0,0 +1,10 @@ +// http://stackoverflow.com/a/26990347 function isSet() from Gajus +export const isSetNotNull = async (fn: () => unknown) => { + let value; + try { + value = await fn(); + } catch (e) { + value = null; + } + return value !== null && value !== undefined; +}; diff --git a/apps/meteor/app/emoji-emojione/lib/rocketchat.js b/apps/meteor/app/emoji-emojione/lib/rocketchat.js deleted file mode 100644 index 7e415b99efe82..0000000000000 --- a/apps/meteor/app/emoji-emojione/lib/rocketchat.js +++ /dev/null @@ -1,280 +0,0 @@ -import emojione from 'emojione'; -import mem from 'mem'; - -import { emojioneRender, emojioneRenderFromShort } from './emojioneRender'; -import { emojisByCategory, emojiCategories, toneList } from './emojiPicker'; - -// TODO remove fix below when issue is solved: https://github.com/joypixels/emojione/issues/617 - -// add missing emojis not provided by JS object, but included on emoji.json -emojione.shortnames += - '|:tm:|:copyright:|:registered:|:digit_zero:|:digit_one:|:digit_two:|:digit_three:|:digit_four:|:digit_five:|:digit_six:|:digit_seven:|:digit_eight:|:digit_nine:|:pound_symbol:|:asterisk_symbol:'; -emojione.regShortNames = new RegExp( - `]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(${emojione.shortnames})`, - 'gi', -); - -emojione.emojioneList[':tm:'] = { - uc_base: '2122', - uc_output: '2122-fe0f', - uc_match: '2122-fe0f', - uc_greedy: '2122-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':copyright:'] = { - uc_base: '00a9', - uc_output: '00a9-f0ef', - uc_match: '00a9-fe0f', - uc_greedy: '00a9-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':registered:'] = { - uc_base: '00ae', - uc_output: '00ae-fe0f', - uc_match: '00ae-fe0f', - uc_greedy: '00ae-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_zero:'] = { - uc_base: '0030', - uc_output: '0030-fe0f', - uc_match: '0030-fe0f', - uc_greedy: '0030-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_one:'] = { - uc_base: '0031', - uc_output: '0031-fe0f', - uc_match: '0031-fe0f', - uc_greedy: '0031-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_two:'] = { - uc_base: '0032', - uc_output: '0032-fe0f', - uc_match: '0032-fe0f', - uc_greedy: '0032-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_three:'] = { - uc_base: '0033', - uc_output: '0033-fe0f', - uc_match: '0033-fe0f', - uc_greedy: '0033-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_four:'] = { - uc_base: '0034', - uc_output: '0034-fe0f', - uc_match: '0034-fe0f', - uc_greedy: '0034-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_five:'] = { - uc_base: '0035', - uc_output: '0035-fe0f', - uc_match: '0035-fe0f', - uc_greedy: '0035-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_six:'] = { - uc_base: '0036', - uc_output: '0036-fe0f', - uc_match: '0036-fe0f', - uc_greedy: '0036-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_seven:'] = { - uc_base: '0037', - uc_output: '0037-fe0f', - uc_match: '0037-fe0f', - uc_greedy: '0037-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_eight:'] = { - uc_base: '0038', - uc_output: '0038-fe0f', - uc_match: '0038-fe0f', - uc_greedy: '0038-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_nine:'] = { - uc_base: '0039', - uc_output: '0039-fe0f', - uc_match: '0039-fe0f', - uc_greedy: '0039-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':pound_symbol:'] = { - uc_base: '0023', - uc_output: '0023-fe0f', - uc_match: '0023-fe0f', - uc_greedy: '0023-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':asterisk_symbol:'] = { - uc_base: '002a', - uc_output: '002a-fe0f', - uc_match: '002a-fe0f', - uc_greedy: '002a-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; -// end fix - -// fix for :+1: - had to replace all function that does its conversion: https://github.com/joypixels/emojione/blob/4.5.0/lib/js/emojione.js#L249 -(function (ns) { - ns.shortnameConversionMap = mem(ns.shortnameConversionMap, { maxAge: 1000 }); - - ns.unicodeCharRegex = mem(ns.unicodeCharRegex, { maxAge: 1000 }); - - const convertShortName = mem( - function (shortname) { - // the fix is basically adding this .replace(/[+]/g, '\\$&') - if (typeof shortname === 'undefined' || shortname === '' || ns.shortnames.indexOf(shortname.replace(/[+]/g, '\\$&')) === -1) { - // if the shortname doesnt exist just return the entire match - return shortname; - } - - // map shortname to parent - if (!ns.emojioneList[shortname]) { - for (const emoji in ns.emojioneList) { - if (!ns.emojioneList.hasOwnProperty(emoji) || emoji === '') { - continue; - } - if (ns.emojioneList[emoji].shortnames.indexOf(shortname) === -1) { - continue; - } - shortname = emoji; - break; - } - } - - const unicode = ns.emojioneList[shortname].uc_output; - const fname = ns.emojioneList[shortname].uc_base; - const category = fname.indexOf('-1f3f') >= 0 ? 'diversity' : ns.emojioneList[shortname].category; - const title = ns.imageTitleTag ? `title="${shortname}"` : ''; - // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; - // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path - const ePath = ns.defaultPathPNG !== ns.imagePathPNG ? ns.imagePathPNG : `${ns.defaultPathPNG + ns.emojiSize}/`; - - // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - const alt = ns.unicodeAlt ? ns.convert(unicode.toUpperCase()) : shortname; - - if (ns.sprites) { - return `${alt}`; - } - return `${alt}`; - }, - { maxAge: 1000 }, - ); - - const convertUnicode = mem( - function (entire, m1, m2, m3) { - const mappedUnicode = ns.mapUnicodeToShort(); - - if (typeof m3 === 'undefined' || m3 === '' || !(ns.unescapeHTML(m3) in ns.asciiList)) { - // if the ascii doesnt exist just return the entire match - return entire; - } - - m3 = ns.unescapeHTML(m3); - const unicode = ns.asciiList[m3]; - const shortname = mappedUnicode[unicode]; - const category = unicode.indexOf('-1f3f') >= 0 ? 'diversity' : ns.emojioneList[shortname].category; - const title = ns.imageTitleTag ? `title="${ns.escapeHTML(m3)}"` : ''; - // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; - // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path - const ePath = ns.defaultPathPNG !== ns.imagePathPNG ? ns.imagePathPNG : `${ns.defaultPathPNG + ns.emojiSize}/`; - - // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - const alt = ns.unicodeAlt ? ns.convert(unicode.toUpperCase()) : ns.escapeHTML(m3); - - if (ns.sprites) { - return `${m2}${alt}`; - } - return `${m2}${alt}`; - }, - { maxAge: 1000, cacheKey: JSON.stringify }, - ); - - ns.shortnameToImage = function (str) { - // replace regular shortnames first - str = str.replace(ns.regShortNames, convertShortName); - - // if ascii smileys are turned on, then we'll replace them! - if (ns.ascii) { - const asciiRX = ns.riskyMatchAscii ? ns.regAsciiRisky : ns.regAscii; - - return str.replace(asciiRX, convertUnicode); - } - - return str; - }; -})(emojione); - -export function getEmojiConfig() { - return { - emojione, - emojisByCategory, - emojiCategories, - toneList, - render: emojioneRender, - renderPicker: emojioneRenderFromShort, - sprites: true, - }; -} - -// http://stackoverflow.com/a/26990347 function isSet() from Gajus -export async function isSetNotNull(fn) { - let value; - try { - value = await fn(); - } catch (e) { - value = null; - } - return value !== null && value !== undefined; -} diff --git a/apps/meteor/app/emoji-emojione/server/lib.ts b/apps/meteor/app/emoji-emojione/server/lib.ts index 4f1808eba60a3..b80b9318a828a 100644 --- a/apps/meteor/app/emoji-emojione/server/lib.ts +++ b/apps/meteor/app/emoji-emojione/server/lib.ts @@ -1,7 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/server'; -import { getEmojiConfig, isSetNotNull } from '../lib/rocketchat'; +import { getEmojiConfig } from '../lib/getEmojiConfig'; +import { isSetNotNull } from '../lib/isSetNotNull'; import { getUserPreference } from '../../utils/server'; const config = getEmojiConfig(); @@ -20,16 +21,11 @@ if (emoji.packages.emojione) { for (const key in config.emojione.emojioneList) { if (config.emojione.emojioneList.hasOwnProperty(key)) { const currentEmoji = config.emojione.emojioneList[key]; - // @ts-expect-error - emojione types currentEmoji.emojiPackage = 'emojione'; - // @ts-expect-error - emojione types emoji.list[key] = currentEmoji; - // @ts-expect-error - emojione types if (currentEmoji.shortnames) { - // @ts-expect-error - emojione types currentEmoji.shortnames.forEach((shortname: string) => { - // @ts-expect-error - emojione types emoji.list[shortname] = currentEmoji; }); } diff --git a/apps/meteor/app/emoji/client/lib.ts b/apps/meteor/app/emoji/client/lib.ts index 55b858cb3e11b..1d2397c9568d3 100644 --- a/apps/meteor/app/emoji/client/lib.ts +++ b/apps/meteor/app/emoji/client/lib.ts @@ -1,4 +1,5 @@ -import { emojioneRender } from '../../emoji-emojione/lib/emojioneRender'; +import emojione from 'emojione'; + import type { EmojiPackages } from '../lib/rocketchat'; export const emoji: EmojiPackages = { @@ -10,7 +11,7 @@ export const emoji: EmojiPackages = { recent: [], }, toneList: {}, - render: emojioneRender, + render: emojione.toImage, renderPicker(emojiToRender) { const correctPackage = emoji.list[emojiToRender].emojiPackage; if (!correctPackage) { diff --git a/apps/meteor/client/contexts/EmojiPickerContext.ts b/apps/meteor/client/contexts/EmojiPickerContext.ts index 50490182d519e..4c4257c3dd3d7 100644 --- a/apps/meteor/client/contexts/EmojiPickerContext.ts +++ b/apps/meteor/client/contexts/EmojiPickerContext.ts @@ -10,7 +10,7 @@ type EmojiPickerContextValue = { handlePreview: (emoji: string, name: string) => void; handleRemovePreview: () => void; addRecentEmoji: (emoji: string) => void; - emojiListByCategory: EmojiByCategory[]; + getEmojiListsByCategory: () => EmojiByCategory[]; recentEmojis: string[]; setRecentEmojis: (emoji: string[]) => void; actualTone: number; @@ -44,16 +44,32 @@ export const usePreviewEmoji = () => ({ handleRemovePreview: useEmojiPickerContext().handleRemovePreview, }); -export const useEmojiPickerData = () => ({ - addRecentEmoji: useEmojiPickerContext().addRecentEmoji, - emojiListByCategory: useEmojiPickerContext().emojiListByCategory, - recentEmojis: useEmojiPickerContext().recentEmojis, - setRecentEmojis: useEmojiPickerContext().setRecentEmojis, - actualTone: useEmojiPickerContext().actualTone, - currentCategory: useEmojiPickerContext().currentCategory, - setCurrentCategory: useEmojiPickerContext().setCurrentCategory, - customItemsLimit: useEmojiPickerContext().customItemsLimit, - setCustomItemsLimit: useEmojiPickerContext().setCustomItemsLimit, - setActualTone: useEmojiPickerContext().setActualTone, - quickReactions: useEmojiPickerContext().quickReactions, -}); +export const useEmojiPickerData = () => { + const { + actualTone, + addRecentEmoji, + currentCategory, + customItemsLimit, + getEmojiListsByCategory, + quickReactions, + recentEmojis, + setActualTone, + setCurrentCategory, + setCustomItemsLimit, + setRecentEmojis, + } = useEmojiPickerContext(); + + return { + addRecentEmoji, + getEmojiListsByCategory, + recentEmojis, + setRecentEmojis, + actualTone, + currentCategory, + setCurrentCategory, + customItemsLimit, + setCustomItemsLimit, + setActualTone, + quickReactions, + }; +}; diff --git a/apps/meteor/client/lib/rooms/roomCoordinator.tsx b/apps/meteor/client/lib/rooms/roomCoordinator.tsx index 9a2e3d14ab475..31eda2982a125 100644 --- a/apps/meteor/client/lib/rooms/roomCoordinator.tsx +++ b/apps/meteor/client/lib/rooms/roomCoordinator.tsx @@ -19,7 +19,7 @@ import type { import { RoomCoordinator } from '../../../lib/rooms/coordinator'; import { router } from '../../providers/RouterProvider'; import RoomRoute from '../../views/room/RoomRoute'; -import MainLayout from '../../views/root/MainLayout/MainLayout'; +import MainLayout from '../../views/root/MainLayout'; import { appLayout } from '../appLayout'; class RoomCoordinatorClient extends RoomCoordinator { diff --git a/apps/meteor/client/providers/EmojiPickerProvider.tsx b/apps/meteor/client/providers/EmojiPickerProvider.tsx index 5c9f3a8e99464..84ffa3394c10a 100644 --- a/apps/meteor/client/providers/EmojiPickerProvider.tsx +++ b/apps/meteor/client/providers/EmojiPickerProvider.tsx @@ -1,5 +1,5 @@ import { useDebouncedState, useLocalStorage } from '@rocket.chat/fuselage-hooks'; -import type { ReactNode, ReactElement } from 'react'; +import type { ReactNode, ReactElement, ContextType } from 'react'; import React, { useState, useCallback, useMemo, useEffect } from 'react'; import type { EmojiByCategory } from '../../app/emoji/client'; @@ -14,7 +14,6 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen const [emojiToPreview, setEmojiToPreview] = useDebouncedState<{ emoji: string; name: string } | null>(null, 100); const [recentEmojis, setRecentEmojis] = useLocalStorage('emoji.recent', []); const [actualTone, setActualTone] = useLocalStorage('emoji.tone', 0); - const [emojiListByCategory, setEmojiListByCategory] = useState([]); const [currentCategory, setCurrentCategory] = useState('recent'); const [customItemsLimit, setCustomItemsLimit] = useState(DEFAULT_ITEMS_LIMIT); @@ -40,26 +39,37 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen [frequentEmojis, setFrequentEmojis], ); + const [getEmojiListsByCategory, setEmojiListsByCategoryGetter] = useState<() => EmojiByCategory[]>(() => () => []); + // TODO: improve this update const updateEmojiListByCategory = useCallback( (categoryKey: string, limit: number = DEFAULT_ITEMS_LIMIT) => { - const result = emojiListByCategory.map((category) => { - return categoryKey === category.key - ? { - ...category, - emojis: { - list: createEmojiList(category.key, null, recentEmojis, setRecentEmojis), - limit: category.key === CUSTOM_CATEGORY ? limit | customItemsLimit : null, - }, - } - : category; - }); - - setEmojiListByCategory(result); + setEmojiListsByCategoryGetter( + (getEmojiListsByCategory) => () => + getEmojiListsByCategory().map((category) => + categoryKey === category.key + ? { + ...category, + emojis: { + list: createEmojiList(category.key, null, recentEmojis, setRecentEmojis), + limit: category.key === CUSTOM_CATEGORY ? limit | customItemsLimit : null, + }, + } + : category, + ), + ); }, - [customItemsLimit, emojiListByCategory, recentEmojis, setRecentEmojis], + [customItemsLimit, recentEmojis, setRecentEmojis], ); + useEffect(() => { + if (recentEmojis?.length > 0) { + updateRecent(recentEmojis); + } + + setEmojiListsByCategoryGetter(() => () => createPickerEmojis(customItemsLimit, actualTone, recentEmojis, setRecentEmojis)); + }, [actualTone, recentEmojis, customItemsLimit, currentCategory, setRecentEmojis, frequentEmojis]); + const addRecentEmoji = useCallback( (_emoji: string) => { addFrequentEmojis(_emoji); @@ -83,21 +93,12 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen [recentEmojis, setRecentEmojis, updateEmojiListByCategory, addFrequentEmojis], ); - useEffect(() => { - if (recentEmojis?.length > 0) { - updateRecent(recentEmojis); - } - - const emojis = createPickerEmojis(customItemsLimit, actualTone, recentEmojis, setRecentEmojis); - setEmojiListByCategory(emojis); - }, [actualTone, recentEmojis, customItemsLimit, currentCategory, setRecentEmojis, frequentEmojis]); - const open = useCallback((ref: Element, callback: (emoji: string) => void) => { return setEmojiPicker( setEmojiPicker(null)} onPickEmoji={(emoji) => callback(emoji)} />); }, []); const contextValue = useMemo( - () => ({ + (): ContextType => ({ isOpen: emojiPicker !== null, close: () => setEmojiPicker(null), open, @@ -105,7 +106,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen handlePreview: (emoji: string, name: string) => setEmojiToPreview({ emoji, name }), handleRemovePreview: () => setEmojiToPreview(null), addRecentEmoji, - emojiListByCategory, + getEmojiListsByCategory, recentEmojis, setRecentEmojis, actualTone, @@ -122,7 +123,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen emojiToPreview, setEmojiToPreview, addRecentEmoji, - emojiListByCategory, + getEmojiListsByCategory, recentEmojis, setRecentEmojis, actualTone, diff --git a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx index cd37a05699fb2..47d8ba8492018 100644 --- a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx +++ b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx @@ -11,7 +11,7 @@ import { } from '@rocket.chat/ui-client'; import { useTranslation, usePermission, useRoute } from '@rocket.chat/ui-contexts'; import type { ChangeEvent, KeyboardEvent, MouseEvent, RefObject } from 'react'; -import React, { useLayoutEffect, useState, useEffect, useRef, useCallback } from 'react'; +import React, { useLayoutEffect, useState, useEffect, useRef } from 'react'; import type { VirtuosoHandle } from 'react-virtuoso'; import type { EmojiItem, EmojiCategoryPosition } from '../../../../app/emoji/client'; @@ -59,13 +59,13 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { setRecentEmojis, actualTone, currentCategory, - emojiListByCategory, + getEmojiListsByCategory, customItemsLimit, setActualTone, setCustomItemsLimit, } = useEmojiPickerData(); - useEffect(() => () => handleRemovePreview(), []); + useEffect(() => () => handleRemovePreview(), [handleRemovePreview]); const scrollCategories = useMediaQuery('(width < 340px)'); @@ -126,16 +126,12 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { onClose(); }; - const showInitialCategory = useCallback((customEmojiList) => { - handleGoToCategory(customEmojiList.length > 0 ? 0 : 1); - }, []); - useEffect(() => { if (recentEmojis.length === 0 && currentCategory === 'recent') { - const customEmojiList = emojiListByCategory.filter(({ key }) => key === 'rocket'); - showInitialCategory(customEmojiList); + const customEmojiList = getEmojiListsByCategory().filter(({ key }) => key === 'rocket'); + handleGoToCategory(customEmojiList.length > 0 ? 0 : 1); } - }, [actualTone, recentEmojis, emojiListByCategory, currentCategory, setRecentEmojis, showInitialCategory]); + }, [actualTone, recentEmojis, getEmojiListsByCategory, currentCategory, setRecentEmojis]); const handleSearch = (e: ChangeEvent) => { setSearchTerm(e.target.value); @@ -216,7 +212,7 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { {!searching && ( fireGlobalEvent('room-opened', omit(room, 'usernames'))); @@ -95,6 +97,8 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st } const { rid } = await createDirectMessage(...reference.split(', ')); + const { ChatSubscription } = await import('../../../../app/models/client'); + const { waitUntilFind } = await import('../../../lib/utils/waitUntilFind'); await waitUntilFind(() => ChatSubscription.findOne({ rid })); directRoute.push({ rid }, (prev) => prev); }, diff --git a/apps/meteor/client/views/root/MainLayout/index.ts b/apps/meteor/client/views/root/MainLayout/index.ts index ed5671ffcf21e..4b36da185ab3b 100644 --- a/apps/meteor/client/views/root/MainLayout/index.ts +++ b/apps/meteor/client/views/root/MainLayout/index.ts @@ -1 +1,5 @@ -export { default } from './MainLayout'; +import { lazy } from 'react'; + +const MainLayout = lazy(() => import('./MainLayout')); + +export default MainLayout; diff --git a/apps/meteor/definition/externals/emojione.d.ts b/apps/meteor/definition/externals/emojione.d.ts new file mode 100644 index 0000000000000..6e9c9deab0c77 --- /dev/null +++ b/apps/meteor/definition/externals/emojione.d.ts @@ -0,0 +1,50 @@ +declare module 'emojione' { + export as namespace emojione; + + export let sprites: boolean; + export let imagePathSVG: string; + export let imagePathSVGSprites: string; + export let imageType: 'png' | 'svg'; + export let unicodeAlt: boolean; + export let ascii: boolean; + export let unicodeRegexp: string; + export let cacheBustParam: string; + export let emojioneList: { + [key: string]: { + uc_base: string; + uc_output: string; + uc_match: string; + uc_greedy: string; + shortnames: string[]; + category: string; + emojiPackage: string; + }; + }; + + type Unicode = string; + type MappedUnicode = Record; + + export let regShortNames: RegExp; + export let shortnames: string; + export const imageTitleTag: boolean; + export const defaultPathPNG: string; + export let imagePathPNG: string; + export const emojiSize: string; + export const fileExtension: string; + export const asciiList: Record; + export const riskyMatchAscii: boolean; + export const regAsciiRisky: RegExp; + export const regAscii: RegExp; + + export function toShort(str: string): string; + export function toImage(str: string): string; + export function shortnameToImage(str: string): string; + export function unicodeToImage(str: string): string; + export function shortnameToUnicode(str: string): string; + export function shortnameConversionMap(): unknown; + export function unicodeCharRegex(): unknown; + export function convert(str: string): string; + export function mapUnicodeToShort(): MappedUnicode; + export function escapeHTML(str: string): string; + export function unescapeHTML(str: string): string; +} diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 0370b5327b4f3..80289d401c386 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -104,7 +104,6 @@ "@types/cssom": "^0.4.1", "@types/dompurify": "^2.3.3", "@types/ejson": "^2.2.0", - "@types/emojione": "^2.2.6", "@types/express": "^4.17.17", "@types/express-rate-limit": "^5.1.3", "@types/fibers": "^3.1.1", diff --git a/yarn.lock b/yarn.lock index 3dcf48ca6520c..49aef6230cd1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10299,7 +10299,6 @@ __metadata: "@types/cssom": ^0.4.1 "@types/dompurify": ^2.3.3 "@types/ejson": ^2.2.0 - "@types/emojione": ^2.2.6 "@types/express": ^4.17.17 "@types/express-rate-limit": ^5.1.3 "@types/fibers": ^3.1.1