From d98371bf6ad6cbf22a7733590181cd4ad32e07a6 Mon Sep 17 00:00:00 2001 From: ewwwwwwww Date: Mon, 4 Jul 2022 13:30:35 -0700 Subject: [PATCH] migrate emoji types --- app/soapbox/actions/compose.ts | 4 +- app/soapbox/actions/emojis.ts | 2 +- app/soapbox/components/autosuggest_emoji.tsx | 18 +--- app/soapbox/components/autosuggest_input.tsx | 3 +- app/soapbox/components/icon_button.js | Bin 5275 -> 5269 bytes .../chats/components/chat-message-list.tsx | 2 +- .../features/chats/components/chat.tsx | 6 +- .../compose/components/compose_form.js | Bin 14449 -> 14458 bytes .../components/emoji_picker_dropdown.tsx | 10 +- .../emoji_picker_dropdown_container.js | Bin app/soapbox/features/emoji/data.ts | 3 + .../features/emoji/emoji_mart_data_light.ts | 51 ---------- .../features/emoji/emoji_mart_search_light.js | Bin 4634 -> 0 bytes .../emoji/emoji_unicode_mapping_light.js | Bin 1146 -> 0 bytes app/soapbox/features/emoji/emoji_utils.js | Bin 6046 -> 0 bytes .../features/emoji/{emoji.ts => index.ts} | 92 +++++++++--------- app/soapbox/features/emoji/mapping.ts | 31 ++++++ app/soapbox/features/emoji/search.ts | 17 ++++ .../features/emoji/unicode_to_filename.js | Bin 660 -> 0 bytes .../features/emoji/unicode_to_unified_name.js | Bin 350 -> 0 bytes .../features/ui/components/link_footer.tsx | 2 +- app/soapbox/normalizers/account.ts | 2 +- app/soapbox/normalizers/poll.ts | 2 +- app/soapbox/normalizers/status_edit.ts | 2 +- app/soapbox/reducers/compose.ts | 2 +- app/soapbox/reducers/custom_emojis.ts | 24 +++-- app/soapbox/reducers/statuses.ts | 2 +- package.json | 2 +- types/emoji-mart/index.d.ts | 15 ++- yarn.lock | 7 +- 30 files changed, 155 insertions(+), 144 deletions(-) rename app/soapbox/features/{compose => emoji}/components/emoji_picker_dropdown.tsx (95%) rename app/soapbox/features/{compose => emoji}/containers/emoji_picker_dropdown_container.js (100%) create mode 100644 app/soapbox/features/emoji/data.ts delete mode 100644 app/soapbox/features/emoji/emoji_mart_data_light.ts delete mode 100644 app/soapbox/features/emoji/emoji_mart_search_light.js delete mode 100644 app/soapbox/features/emoji/emoji_unicode_mapping_light.js delete mode 100644 app/soapbox/features/emoji/emoji_utils.js rename app/soapbox/features/emoji/{emoji.ts => index.ts} (58%) create mode 100644 app/soapbox/features/emoji/mapping.ts create mode 100644 app/soapbox/features/emoji/search.ts delete mode 100644 app/soapbox/features/emoji/unicode_to_filename.js delete mode 100644 app/soapbox/features/emoji/unicode_to_unified_name.js diff --git a/app/soapbox/actions/compose.ts b/app/soapbox/actions/compose.ts index d37330d289..23913de151 100644 --- a/app/soapbox/actions/compose.ts +++ b/app/soapbox/actions/compose.ts @@ -4,7 +4,7 @@ import { defineMessages, IntlShape } from 'react-intl'; import snackbar from 'soapbox/actions/snackbar'; import api from 'soapbox/api'; -import { search as emojiSearch } from 'soapbox/features/emoji/emoji_mart_search_light'; +import emojiSearch from 'soapbox/features/emoji/search'; import { tagHistory } from 'soapbox/settings'; import { isLoggedIn } from 'soapbox/utils/auth'; import { getFeatures, parseVersion } from 'soapbox/utils/features'; @@ -20,8 +20,8 @@ import { getSettings } from './settings'; import { createStatus } from './statuses'; import type { History } from 'history'; -import type { Emoji } from 'soapbox/components/autosuggest_emoji'; import type { AutoSuggestion } from 'soapbox/components/autosuggest_input'; +import type { Emoji } from 'soapbox/features/emoji'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { Account, APIEntity, Status } from 'soapbox/types/entities'; diff --git a/app/soapbox/actions/emojis.ts b/app/soapbox/actions/emojis.ts index 04bda6c899..f3d33ac615 100644 --- a/app/soapbox/actions/emojis.ts +++ b/app/soapbox/actions/emojis.ts @@ -1,6 +1,6 @@ import { saveSettings } from './settings'; -import type { Emoji } from 'soapbox/components/autosuggest_emoji'; +import type { Emoji } from 'soapbox/features/emoji'; import type { AppDispatch } from 'soapbox/store'; const EMOJI_USE = 'EMOJI_USE'; diff --git a/app/soapbox/components/autosuggest_emoji.tsx b/app/soapbox/components/autosuggest_emoji.tsx index 22979d4542..5e3b37ff00 100644 --- a/app/soapbox/components/autosuggest_emoji.tsx +++ b/app/soapbox/components/autosuggest_emoji.tsx @@ -1,17 +1,10 @@ import React from 'react'; -import unicodeMapping from 'soapbox/features/emoji/emoji_unicode_mapping_light'; +import type { Emoji } from 'soapbox/features/emoji'; +import unicodeMapping from 'soapbox/features/emoji/mapping'; import { joinPublicPath } from 'soapbox/utils/static'; -export type Emoji = { - id: string, - custom: boolean, - imageUrl: string, - native: string, - colons: string, -} - -type UnicodeMapping = { +interface UnicodeMapping { filename: string, } @@ -25,14 +18,13 @@ const AutosuggestEmoji: React.FC = ({ emoji }) => { if (emoji.custom) { url = emoji.imageUrl; } else { - // @ts-ignore - const mapping: UnicodeMapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')]; + const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')]; if (!mapping) { return null; } - url = joinPublicPath(`packs/emoji/${mapping.filename}.svg`); + url = joinPublicPath(`packs/emoji/${mapping.unified}.svg`); } return ( diff --git a/app/soapbox/components/autosuggest_input.tsx b/app/soapbox/components/autosuggest_input.tsx index cd2c74a07e..a8b334d3fd 100644 --- a/app/soapbox/components/autosuggest_input.tsx +++ b/app/soapbox/components/autosuggest_input.tsx @@ -3,11 +3,12 @@ import { List as ImmutableList } from 'immutable'; import React from 'react'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import AutosuggestEmoji, { Emoji } from 'soapbox/components/autosuggest_emoji'; +import AutosuggestEmoji from 'soapbox/components/autosuggest_emoji'; import Icon from 'soapbox/components/icon'; import AutosuggestAccount from 'soapbox/features/compose/components/autosuggest_account'; import { isRtl } from 'soapbox/rtl'; +import type { Emoji } from 'soapbox/features/emoji'; import type { Menu, MenuItem } from 'soapbox/components/dropdown_menu'; type CursorMatch = [ diff --git a/app/soapbox/components/icon_button.js b/app/soapbox/components/icon_button.js index b6ed8b3d46657692654f826527ac6df76b095eda..b8434018e3d484b80b8888290355babd6a10cf37 100644 GIT binary patch delta 10 ScmbQOIaPDQtBoI43j+WgKLuO> delta 10 ScmbQLIa_nWtBp@K3IhNf8wFMX diff --git a/app/soapbox/features/chats/components/chat-message-list.tsx b/app/soapbox/features/chats/components/chat-message-list.tsx index 935a3707d7..28e065ccc2 100644 --- a/app/soapbox/features/chats/components/chat-message-list.tsx +++ b/app/soapbox/features/chats/components/chat-message-list.tsx @@ -15,7 +15,7 @@ import { openModal } from 'soapbox/actions/modals'; import { initReportById } from 'soapbox/actions/reports'; import { Text } from 'soapbox/components/ui'; import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import Bundle from 'soapbox/features/ui/components/bundle'; import { MediaGallery } from 'soapbox/features/ui/util/async-components'; import { useAppSelector, useAppDispatch, useRefEventHandler } from 'soapbox/hooks'; diff --git a/app/soapbox/features/chats/components/chat.tsx b/app/soapbox/features/chats/components/chat.tsx index e2e4e90c19..6eed61fb27 100644 --- a/app/soapbox/features/chats/components/chat.tsx +++ b/app/soapbox/features/chats/components/chat.tsx @@ -5,7 +5,7 @@ import Avatar from 'soapbox/components/avatar'; import DisplayName from 'soapbox/components/display-name'; import Icon from 'soapbox/components/icon'; import { Counter } from 'soapbox/components/ui'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import { useAppSelector } from 'soapbox/hooks'; import { makeGetChat } from 'soapbox/selectors'; @@ -30,7 +30,9 @@ const Chat: React.FC = ({ chatId, onClick }) => { const content = chat.getIn(['last_message', 'content']); const attachment = chat.getIn(['last_message', 'attachment']); const image = attachment && (attachment as any).getIn(['pleroma', 'mime_type'], '').startsWith('image/'); - const parsedContent = content ? emojify(content) : ''; + + // TODO: fix chat.getIn typings + const parsedContent = content ? emojify(content as string) : ''; return (
diff --git a/app/soapbox/features/compose/components/compose_form.js b/app/soapbox/features/compose/components/compose_form.js index 69dffd7de2fdf76eebc97dc5360393eeec02b5ae..0bd15ab7ad6145b0371c998e1648f18cb821faf8 100644 GIT binary patch delta 33 pcmexZ@T*|M9;V5?%(5JMditrk`B|BhS2CMU-otcp^F8LtCII)D4iW$W delta 28 kcmexW@UdXS9;V4+%oiu`VUnF}#mYVTEtBTv=ggB$0KpIp5dZ)H diff --git a/app/soapbox/features/compose/components/emoji_picker_dropdown.tsx b/app/soapbox/features/emoji/components/emoji_picker_dropdown.tsx similarity index 95% rename from app/soapbox/features/compose/components/emoji_picker_dropdown.tsx rename to app/soapbox/features/emoji/components/emoji_picker_dropdown.tsx index 31722a5335..d47ed3a8bb 100644 --- a/app/soapbox/features/compose/components/emoji_picker_dropdown.tsx +++ b/app/soapbox/features/emoji/components/emoji_picker_dropdown.tsx @@ -8,11 +8,12 @@ import { usePopper } from 'react-popper'; import { IconButton } from 'soapbox/components/ui'; import { useSettings } from 'soapbox/hooks'; -import { buildCustomEmojis } from '../../emoji/emoji'; +import { buildCustomEmojis } from '../../emoji'; import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components'; -// import EmojiPicker from '../../emoji/emoji_picker'; +// import { Picker as EmojiPicker } from '../../emoji/emoji_picker'; import type { List } from 'immutable'; +import type { Emoji } from 'soapbox/features/emoji'; let EmojiPicker: any; // load asynchronously @@ -37,7 +38,7 @@ interface IEmojiPickerDropdown { custom_emojis: List, frequentlyUsedEmojis: string[], intl: any, - onPickEmoji: (emoji: any) => void, + onPickEmoji: (emoji: Emoji) => void, onSkinTone: () => void, skinTone: () => void, } @@ -72,7 +73,8 @@ const EmojiPickerDropdown: React.FC = ({ custom_emojis, fr } }; - const handlePick = (emoji: any) => { + const handlePick = (emoji: Emoji) => { + // TODO: remove me if (!emoji.native) { emoji.native = emoji.shortcodes; } diff --git a/app/soapbox/features/compose/containers/emoji_picker_dropdown_container.js b/app/soapbox/features/emoji/containers/emoji_picker_dropdown_container.js similarity index 100% rename from app/soapbox/features/compose/containers/emoji_picker_dropdown_container.js rename to app/soapbox/features/emoji/containers/emoji_picker_dropdown_container.js diff --git a/app/soapbox/features/emoji/data.ts b/app/soapbox/features/emoji/data.ts new file mode 100644 index 0000000000..ac5d3d0daf --- /dev/null +++ b/app/soapbox/features/emoji/data.ts @@ -0,0 +1,3 @@ +import data from '@emoji-mart/data/sets/14/twitter.json'; + +export default data; diff --git a/app/soapbox/features/emoji/emoji_mart_data_light.ts b/app/soapbox/features/emoji/emoji_mart_data_light.ts deleted file mode 100644 index 16d4107de0..0000000000 --- a/app/soapbox/features/emoji/emoji_mart_data_light.ts +++ /dev/null @@ -1,51 +0,0 @@ -// The output of this module is designed to mimic emoji-mart's -// "data" object, such that we can use it for a light version of emoji-mart's -// emojiIndex.search functionality. -// import emojiCompressed from './emoji_compressed'; -import { unicodeToUnifiedName } from './unicode_to_unified_name'; - -const [ shortCodesToEmojiData, skins, categories, short_names ] = [ - [], - [], - [], - [], -]; - -const emojis: Record = {}; - -// decompress -Object.keys(shortCodesToEmojiData).forEach((shortCode) => { - // @ts-ignore - const [ - _filenameData, // eslint-disable-line @typescript-eslint/no-unused-vars - searchData, - // @ts-ignore - ] = shortCodesToEmojiData[shortCode]; - const [ - native, - short_names, - search, - unified, - ] = searchData; - - emojis[shortCode] = { - native, - search, - short_names: [shortCode].concat(short_names), - unified: unified || unicodeToUnifiedName(native), - }; -}); - -export { - emojis, - skins, - categories, - short_names, -}; - -export default { - emojis, - skins, - categories, - short_names, -}; diff --git a/app/soapbox/features/emoji/emoji_mart_search_light.js b/app/soapbox/features/emoji/emoji_mart_search_light.js deleted file mode 100644 index 89e25785f8cc5dce8f420d6d394fbd594ade0d02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4634 zcmb_gTW{Mo6n^)wU|(t(v8ALyF`${#hpqzx6j-qqdvSw6mS~%SEO{c;CUf%NcW$I8 z#VxjB1_I0Exqj!WWSRQBGgcK%sTF)zdArrsgIYIj+kDfd+O*C7%_Pgz&bfnqlVw}u zcHKHHntir6)|#(6)BEPmEcSWpvUSz0v)7wfn@v$<>-r zRGGV+G^lCHG`A?_7SC@hv)#GrrKdfrt#-e_#T*|#<+X9uNsDNu+o2+4}hL6)aGD!T&+Z|i)o z?Ht+IB5-}#3+d@i3Fi^Z>KqMI%4}2;nAs53OH0A>_j$1kBx-p@#wz#>SP=;{lB~G# zf{?7$u4`+w1V2xx6hEGpfa*EHN>eiN(+t0f0f+_Cl__t4W}E_vDTz9ctdJ5fB6>Li zS(~mdoM~!RR61|pMFC3=kPQ-w=olRfFdz^idh2$x z?;%6!!c6}=9+FCT20(oIQoR@e(|h6Pyp z2*t-8;zS%Qq!bj(*SzZVTpc+B?C0jwuKD(>sh~T})jq%fUE8j5P_?Ems;&f5eJ>BJ zii-I7G>(;ev0SPSn$*VBy7aJ|iX~C9tQnY%pYP?!AUUYul&-9nDi?c}3j3H8WA6cA z6a9dc|LHZOMBc!yIEdB20>n|c@=tOF!m)$43=Q1ZT~*D=c8oe4#D<2}MNo&#JrX?v zLg#7;cuf~mR39H)Og(nKJ1MS0?nTqZ#q>tXk-86?MiML-!a7~^0S09IF1I?FrS?!6 zmt?Ci_Hq9BW$}1>bC%86e1N{HB;>$N0|5gP%(P;^x>B!t!-BdMKS9HuvueA#7@3En z1Zpn}To!~!=qTS6aOx3W&~$Ln6uHw|09o5m-g5NN_d7J4d!Dh^oW0oge4L?nM|Qjo z2UI)1Z4xgu#nF`xV4ML2iwpHexwg}oN&N8&-*c5vpXEnv7DG4H+i>u26mD|J`CcOe z`XcfW&@|So?_-4K4ab|5h}g}DKFACYuu4ZqRL33d*~o{+FFh)BC=`wca&0~fuMZGk zU;o!IO_?7`U1cd}XTbn(Em7lI#m37|NP6rM43Z}u5k;b? zW>`QfZ9KNor==|#xCJp$|3MW)PPpfRG1Q(S*3WsP{Tfy;Sgt8FL+xHa6(4$m(Zdz7 z4-xBv&6>t|ze$fNY_|phgmVhd*0ss`T+j_o-y04DKC~=1rNn?n2sgZPcv3Fwz<|?! zaWVFSbB|C2BLeZ6y68j20x=+PPg4-qW951J1oVoB*r$ii(#9XH+)>#uldxbs;ypXc z2d3k23km%gn(2!Wsig!53M7+K3okoK0xy5-K^-zJ=YReQQwu?%6kFx6hd+z%dPKBF zqLJ0y(v`?tDCUXn=2sI|9$D=>LIjTI3RGS=C50~(rt$+B1II((PxC7k<$*wgj#8ph zo01+Sg5nhpM-=Uwz~F02tQjmOqH&)el1T0>-O%EizzgY>%6-D3i9Pp8Gy7{$9A3@S z{BWoq658&f#Ypn&i48v~y)T4;&xDeTc1J!GgS#lL^-#;?QR;JL{qWWds Q;|<-N9G9#V1^ANrH^(E4uK)l5 diff --git a/app/soapbox/features/emoji/emoji_unicode_mapping_light.js b/app/soapbox/features/emoji/emoji_unicode_mapping_light.js deleted file mode 100644 index e340c349734e333318a7de042b6a233e743636a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1146 zcmbtTO>dh(5WVv&M!5te1NTO*8nvlAx-R zzXO(7p<@Wm-bgo@%azXk@_ThNh;|(8UeMRxOE?KagTGtbV0fLReIC|K>%s2pNB~6395brw2EFe+ z=xgu$G|l07lFDuCGPdNPZYY!D&KapubHZ+r{%1BB7{O6RKRXM~usz^xOcm&qmz}}_ z!zexb^4z4A3Uv_<6$VDE591d+Jq;L>9N`%z(k2<^6a+uItB~yR(T8KKF9R1PTWao1 zR)o`j1oMy2MjEtK5xQ|+gj$ZP;@Na&Z5l}~bx@Yqs4PaF diff --git a/app/soapbox/features/emoji/emoji_utils.js b/app/soapbox/features/emoji/emoji_utils.js deleted file mode 100644 index 1f4629edf92efe1a061545dd0a1685eb07a835f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6046 zcmb_g?Q`3<8UOCT0=aq>b(Sc*>+UusaV~M}o!7?B)$P`s>`D*`NpvVeBOu$FX#2O1&KJ4F+Oi?aB-X zgL!5b>uH?mh$^M&oa+}lfZVXT<0vpl_0vP>4@wTK9$D_$Rq$2|eL z3BrWU(G9S|s!Y?Lb&Y3inH6)>6K0iXwxS?JHWSeczuRT10>4t(by>UwH6fmes1VWH zvXq-RGyIGylsCe^ilG?!*PCuU)8(m57Lf;Ra0Dq9LIlj1Kcm5@|9SlR+T0urx;3p9 zsTLI>3@l<|tAB*E$`ff+)MX2hxK1-?o0vUtIg1PkynZeEN42H|>7iC@vjB+Gn%19K zaXkH+dpi<3cVKtZd7*Vx(ta1n_(NpVa1Fmnmq>WNyAd1Tkkb@_r2k zQJvF*Hn%z}qE4>^F-T$;E8{DU1K7>IR2$B5XNdO`B*RDGS^#nH(?M!E&yobCGV7c)b<6jQELS2r zIB*PKK}up0NDu~T63lb4UQSgRRRW9{?Z!t7$pBXLdy$#54CLhlNg(fv?gCKvKz z(i4+|^I}GT8+_nn5$C+LK$_=3nx5F z&O#4GM;z2NLozZYGIGuB6-6E?0|o-D-+lPFTX~Mfm=nIj4s?Y=-Z}iYNDG-aNW66Q z6Uqn^0#__#ey5B#Ex1y%K(AQBJef4pO`!@CPt^cMRc3H2@1#k}Y-RhZ0CSY(Zz@`S zl+EQjm+G0L`lQ+sl8Up6{N2hPgmA+S$H(I6unVIdKD_^HjnPc4Ho~6(iH!9qCp8LL zr(vuX&z@)!4Cbrc;&SLf9C6L{>58DQN&2;85h_O=7d|l$LZNacXd%lJ2o%fJD&IuK zI?sDGIWGW}oFKDj0Mk*@yR@gG?rVW#(yEXDxVpT6Y3Nba#*@n*FE6gfKb`*l^jGW+ zev94=uZPj-x9j1}LHBytWQ zdhX?2csc3x!sh$1b{byJuu085lwGkdvRS6k;V-1c5EA)sXFpeRu|$TTitBfMpY8NJ zUE<{o#kI@>F?_s~t0-D?tDPJ^>_4ubCNOPnwH>YEnL`{^pka8ZgfUR6!@^+7}4<5Tc)(XZ<`+>Re85Rbt~+sjVi};)OJq_r=FjuT*I_aU%14$8^g|b!+DQ4c>G( zY0kkT-hl9qIY0gwTPA=hcPB^h9_H!*w-`cW*UX72Lm=ye*ndorMxF4|5UD`RyNqhe zm#l!j|C=Fpu>O)QYPMB`aG)Sbok8d;ILH?ovdjw~8aR#^XFB50^&@uBn@M=q{O%}K z%dxA;FbW-rd=NAF8pIvG0!430q8Ym#AUDpyzISX%TvNlFI~f6O*DCaO=Z|``?kg>v z#d`%TBekQiWovb^-+#RasxrCAmfu zl$vq2oR86tlzo~sYFW*a^yCo$n!7+{YC>R4rg@jHKR(eg-lAQL7uxFtshKRewK;g}1mTo}2yuLA3u6dsPHmD8H&hPkRDX8{H^Soi?~N+Pd1_vEY<5)*djn3CrZGl+5qTT-A@MHEQTo8Y%(+wS|RFR(GMk { - const result = {}; - const emojis = Object.values(data.emojis ?? {}); - - for (const value of emojis) { - // @ts-ignore - for (const item of value.skins) { - const { unified, native } = item; - - // @ts-ignore - result[native] = { unified, shortcode: value.id }; - } - } - - return result; -}; - -const unicodeMapping = generateMappings(data); +export type Emoji = any; const isAlphaNumeric = (c: string) => { const code = c.charCodeAt(0); - if (!(code > 47 && code < 58) && // numeric (0-9) - !(code > 64 && code < 91) && // upper alpha (A-Z) + if (!(code > 47 && code < 58) && // numeric (0-9) + !(code > 64 && code < 91) && // upper alpha (A-Z) !(code > 96 && code < 123)) { // lower alpha (a-z) return false; } else { @@ -59,11 +45,11 @@ const convertUnicode = (c: string) => { return `${c}`; }; -const convertEmoji = (str: string, customEmojis: any, autoplay: boolean) => { +const convertEmoji = (str: string, customEmojis: any) => { if (str.length < 3) return str; if (str in customEmojis) { const emoji = customEmojis[str]; - const filename = autoplay ? emoji.url : emoji.static_url; + const filename = emoji.static_url; if (filename?.length > 0) { return convertCustom(str, filename); @@ -73,29 +59,33 @@ const convertEmoji = (str: string, customEmojis: any, autoplay: boolean) => { return str; }; -const popStack = (stack: string, open: boolean, res: string) => { - res += stack; +const popStack = (stack: string, open: boolean) => { + const ret = stack; open = false; stack = ''; + return ret; }; -const transmogrify = (str: string, customEmojis = {}, autoplay: boolean) => { +// TODO: handle grouped unicode emojis +export const emojifyText = (str: string, customEmojis = {}) => { let res = ''; let stack = ''; let open = false; - for (const c of Array.from(str)) { // Array.from respects unicode + for (const c of Array.from(str)) { // chunk by unicode codepoint with Array.from if (c in unicodeMapping) { if (open) { // unicode emoji inside colon - popStack(stack, open, res); + res += popStack(stack, open); } res += convertUnicode(c); + } else if (c === ':') { stack += ':'; + // we see another : we convert it and clear the stack buffer if (open) { - res += convertEmoji(stack, customEmojis, autoplay); + res += convertEmoji(stack, customEmojis); stack = ''; } @@ -104,8 +94,10 @@ const transmogrify = (str: string, customEmojis = {}, autoplay: boolean) => { if (open) { stack += c; + // if the stack is non-null and we see invalid chars it's a string not emoji + // so we push it to the return result and clear it if (!validEmojiChar(c)) { - popStack(stack, open, res); + res += popStack(stack, open); } } else { res += c; @@ -113,14 +105,26 @@ const transmogrify = (str: string, customEmojis = {}, autoplay: boolean) => { } } + // never found a closing colon so it's just a raw string + if (open) { + res += stack; + } + return res; }; +// const parseHmtl = (str: string) => { +// const ret = []; +// let depth = 0; +// +// return ret; +// } + const filterTextNodes = (idx: number, el: CheerioNode) => { return el.nodeType === Node.TEXT_NODE; }; -const emojify = (str: string | any, customEmojis = {}, autoplay = false) => { +const emojify = (str: string, customEmojis = {}) => { const dom = parseDocument(str); const $ = cheerioLoad(dom, { xmlMode: true, @@ -128,8 +132,8 @@ const emojify = (str: string | any, customEmojis = {}, autoplay = false) => { }); $.root() - .contents() - .filter(filterTextNodes) + .contents() // iterate over flat map of all html elements + .filter(filterTextNodes) // only iterate over text nodes .each((idx, el) => { // skip common case // @ts-ignore @@ -137,7 +141,7 @@ const emojify = (str: string | any, customEmojis = {}, autoplay = false) => { // mutating el.data is incorrect but we do it to prevent a second dom parse // @ts-ignore - el.data = transmogrify(el.data, customEmojis, autoplay); + el.data = emojifyText(el.data, customEmojis); }); return $.html(); @@ -145,12 +149,12 @@ const emojify = (str: string | any, customEmojis = {}, autoplay = false) => { export default emojify; -export const buildCustomEmojis = (customEmojis: any, autoplay = false) => { - const emojis: any[] = []; +export const buildCustomEmojis = (customEmojis: any) => { + const emojis: EmojiMartEmoji[] = []; customEmojis.forEach((emoji: any) => { const shortcode = emoji.get('shortcode'); - const url = autoplay ? emoji.get('url') : emoji.get('static_url'); + const url = emoji.get('static_url'); const name = shortcode.replace(':', ''); emojis.push({ diff --git a/app/soapbox/features/emoji/mapping.ts b/app/soapbox/features/emoji/mapping.ts new file mode 100644 index 0000000000..413db7e2d3 --- /dev/null +++ b/app/soapbox/features/emoji/mapping.ts @@ -0,0 +1,31 @@ +import emojiData from './data'; + +import type EmojiData from '@emoji-mart/data'; + +interface IUniMap { + [s: string]: { + unified: string, + shortcode: string, + } +} + +export const generateMappings = (data: typeof EmojiData): IUniMap => { + const result = {}; + const emojis = Object.values(data.emojis ?? {}); + + for (const value of emojis) { + // @ts-ignore + for (const item of value.skins) { + const { unified, native } = item; + + // @ts-ignore + result[native] = { unified, shortcode: value.id }; + } + } + + return result; +}; + +const unicodeMapping = generateMappings(emojiData); + +export default unicodeMapping; diff --git a/app/soapbox/features/emoji/search.ts b/app/soapbox/features/emoji/search.ts new file mode 100644 index 0000000000..ef31f1e276 --- /dev/null +++ b/app/soapbox/features/emoji/search.ts @@ -0,0 +1,17 @@ +export interface searchOptions { + maxResults?: number; +} + +export interface Emoji { + +} + +export const addCustomToPool = (customEmojis: Emoji[]) => { +}; + +const search = (str: string, options: searchOptions) => { + console.log(str, options); + return []; +}; + +export default search; diff --git a/app/soapbox/features/emoji/unicode_to_filename.js b/app/soapbox/features/emoji/unicode_to_filename.js deleted file mode 100644 index c75c4cd7d05fe1bb47303bb673d79dccdba8951a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmb`D!ES;;5QgtO#UxF134z7dC{Z+NZ9I8vUjXR}Yk?)p5KWBl-dSLahaO8pmid47 zn}3FLz~oz0aMG$=Ih;d|ST`%qGl_Y73{q8cyhuc$=@g|pOMWcWk`m4v{*ReS}ToB&{i^4U*c2kWg)7h6tDr-pmt%ig_{FV2m~#fwm@>G z)Ab;wd7^i8k;Kqy^@J?#OflR3E5i zm9g1k$OrH{IQ0QhG@{Gg^B~@gQ%)x1H|g}p+kQW0HyqoupMAjHhV*6?|1rC5^2ao0 RQRdYGTU`yyTs!x*^8;gowqgJP diff --git a/app/soapbox/features/emoji/unicode_to_unified_name.js b/app/soapbox/features/emoji/unicode_to_unified_name.js deleted file mode 100644 index d29550f122683043fed10a5ce0dc222ec3d320b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmZ9IL2Cmc5QXpiE8cCDB+IsxUd#qcub~v${D37pCIcH4cO-?9|6a{DG}MDS-*cW% zZE|~I?-2T7OIFk*aS4}B8s8OgKJrK?uo=l6#&U RBBhsj(*IvQr#IEbR6oaiX1)Lb diff --git a/app/soapbox/features/ui/components/link_footer.tsx b/app/soapbox/features/ui/components/link_footer.tsx index c2104fbcd4..d3ee72eb10 100644 --- a/app/soapbox/features/ui/components/link_footer.tsx +++ b/app/soapbox/features/ui/components/link_footer.tsx @@ -6,7 +6,7 @@ import { Link } from 'react-router-dom'; import { logOut } from 'soapbox/actions/auth'; import { Text } from 'soapbox/components/ui'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import { useSoapboxConfig, useOwnAccount, useFeatures } from 'soapbox/hooks'; import sourceCode from 'soapbox/utils/code'; diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 1a519b8a81..948787e4a3 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -11,7 +11,7 @@ import { fromJS, } from 'immutable'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import { normalizeEmoji } from 'soapbox/normalizers/emoji'; import { unescapeHTML } from 'soapbox/utils/html'; import { mergeDefined, makeEmojiMap } from 'soapbox/utils/normalizers'; diff --git a/app/soapbox/normalizers/poll.ts b/app/soapbox/normalizers/poll.ts index 7b98d1354f..ec74071952 100644 --- a/app/soapbox/normalizers/poll.ts +++ b/app/soapbox/normalizers/poll.ts @@ -11,7 +11,7 @@ import { fromJS, } from 'immutable'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import { normalizeEmoji } from 'soapbox/normalizers/emoji'; import { makeEmojiMap } from 'soapbox/utils/normalizers'; diff --git a/app/soapbox/normalizers/status_edit.ts b/app/soapbox/normalizers/status_edit.ts index 7bf38adc18..6f5d8d53ae 100644 --- a/app/soapbox/normalizers/status_edit.ts +++ b/app/soapbox/normalizers/status_edit.ts @@ -9,7 +9,7 @@ import { fromJS, } from 'immutable'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import { normalizeAttachment } from 'soapbox/normalizers/attachment'; import { normalizeEmoji } from 'soapbox/normalizers/emoji'; import { normalizePoll } from 'soapbox/normalizers/poll'; diff --git a/app/soapbox/reducers/compose.ts b/app/soapbox/reducers/compose.ts index 3b861fb583..26a96a1899 100644 --- a/app/soapbox/reducers/compose.ts +++ b/app/soapbox/reducers/compose.ts @@ -59,7 +59,7 @@ import { normalizeAttachment } from '../normalizers/attachment'; import { unescapeHTML } from '../utils/html'; import type { AnyAction } from 'redux'; -import type { Emoji } from 'soapbox/components/autosuggest_emoji'; +import type { Emoji } from 'soapbox/features/emoji'; import type { Account as AccountEntity, APIEntity, diff --git a/app/soapbox/reducers/custom_emojis.ts b/app/soapbox/reducers/custom_emojis.ts index 477e7cce97..680088805f 100644 --- a/app/soapbox/reducers/custom_emojis.ts +++ b/app/soapbox/reducers/custom_emojis.ts @@ -1,10 +1,10 @@ import { List as ImmutableList, Map as ImmutableMap, fromJS } from 'immutable'; -import { emojis as emojiData } from 'soapbox/features/emoji/emoji_mart_data_light'; -import { addCustomToPool } from 'soapbox/features/emoji/emoji_mart_search_light'; +import { buildCustomEmojis } from 'soapbox/features/emoji'; +import { addCustomToPool } from 'soapbox/features/emoji/search'; +// import emojiData from 'soapbox/features/emoji/data'; import { CUSTOM_EMOJIS_FETCH_SUCCESS } from '../actions/custom_emojis'; -import { buildCustomEmojis } from '../features/emoji/emoji'; import type { AnyAction } from 'redux'; import type { APIEntity } from 'soapbox/types/entities'; @@ -17,14 +17,18 @@ const autosuggestPopulate = (emojis: ImmutableList> }; const importEmojis = (customEmojis: APIEntity[]) => { - const emojis = (fromJS(customEmojis) as ImmutableList>).filter((emoji) => { - // If a custom emoji has the shortcode of a Unicode emoji, skip it. - // Otherwise it breaks EmojiMart. - // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/610 - const shortcode = emoji.get('shortcode', '').toLowerCase(); - return !emojiData[shortcode]; - }); + // const emojis = (fromJS(customEmojis)).filter((emoji) => { + // // If a custom emoji has the shortcode of a Unicode emoji, skip it. + // // Otherwise it breaks EmojiMart. + // // https://gitlab.com/soapbox-pub/soapbox-fe/-/issues/610 + // const shortcode = emoji.get('shortcode', '').toLowerCase(); + // return !emojiData.emojis[shortcode]; + // }); + // @ts-ignore + const emojis = fromJS(customEmojis); + + // @ts-ignore autosuggestPopulate(emojis); return emojis; }; diff --git a/app/soapbox/reducers/statuses.ts b/app/soapbox/reducers/statuses.ts index 22b9e5ac0a..8f2666db00 100644 --- a/app/soapbox/reducers/statuses.ts +++ b/app/soapbox/reducers/statuses.ts @@ -1,7 +1,7 @@ import escapeTextContentForBrowser from 'escape-html'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; -import emojify from 'soapbox/features/emoji/emoji'; +import emojify from 'soapbox/features/emoji'; import { normalizeStatus } from 'soapbox/normalizers'; import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts'; import { stripCompatibilityFeatures, unescapeHTML } from 'soapbox/utils/html'; diff --git a/package.json b/package.json index ed0aa835a4..240b31ee40 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "emoji-datasource": "5.0.0", "emoji-mart": "^5.1.0", "emoji-mart-old": "npm:emoji-mart-lazyload", - "entities": "^3.0.1", + "entities": "^4.3.1", "es6-symbol": "^3.1.1", "escape-html": "^1.0.3", "exif-js": "^2.3.0", diff --git a/types/emoji-mart/index.d.ts b/types/emoji-mart/index.d.ts index 291e4e7d27..0a9b928554 100644 --- a/types/emoji-mart/index.d.ts +++ b/types/emoji-mart/index.d.ts @@ -1,6 +1,17 @@ declare module 'emoji-mart' { - export type PickerProps = { - custom?: { emojis: any[] }[], + export interface EmojiSkin { + src: string + } + + export interface Emoji { + id: string, + name: string, + keywords: string[], + skins: EmojiSkin[], + } + + export interface PickerProps { + custom?: { emojis: Emoji[] }[], set?: string, title?: string, theme?: string, diff --git a/yarn.lock b/yarn.lock index 9bebc92a1b..4963eb84da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,12 +5184,7 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== - -entities@^4.2.0, entities@^4.3.0: +entities@^4.2.0, entities@^4.3.0, entities@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4" integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==