From 761f59645dfcb55f13570ba0b05cf22c5333b084 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Mon, 9 May 2016 03:17:26 -0400 Subject: [PATCH] PLT-2816 Fixed handling of Unicode 8 emojis (#2924) * Updated twemoji to properly recognize Unicode 8.0 emojis * Updated unicode emoji parser to only render emojis we support as images * Corrected filename for South African flag emoji * Added Mattermost emoticons! * Added additional emoticons to test files --- tests/test-emoticons1.md | 4 +- tests/test-emoticons3.md | 2 +- .../suggestion/emoticon_provider.jsx | 2 +- .../{1f1ff-1e1e6.png => 1f1ff-1f1e6.png} | Bin webapp/images/emoji/mm.png | Bin 0 -> 6576 bytes webapp/package.json | 2 +- webapp/utils/emoji.json | 8 ++++ webapp/utils/emoticons.jsx | 41 +++++++++++++----- webapp/utils/text_formatting.jsx | 16 +++++-- 9 files changed, 57 insertions(+), 18 deletions(-) rename webapp/images/emoji/{1f1ff-1e1e6.png => 1f1ff-1f1e6.png} (100%) create mode 100644 webapp/images/emoji/mm.png diff --git a/tests/test-emoticons1.md b/tests/test-emoticons1.md index 855edbb1ca..57b4f827ae 100644 --- a/tests/test-emoticons1.md +++ b/tests/test-emoticons1.md @@ -1,12 +1,14 @@ # Emoticon Testing Verify that all emoticons render. This test should render in three separate messages since it's ~11000 characters. +:mm: :mattermost: + ### Emoticon - Punctuation :) :-) ;) ;-) :o :O :-o :-O :] :-] :d :-D x-d x-D :p :-P :@ :( :-( :'( :/ :-/ :s :-s :| :-| :$ :-$ :-x <3 :+1: :-1: ### Emoticons - People -:bowtie: :smile: :laughing: :blush: :smiley: :relaxed: :smirk: :heart_eyes: :kissing_heart: :kissing_closed_eyes: :flushed: :relieved: :satisfied: :grin: :wink: :stuck_out_tongue_winking_eye: :stuck_out_tongue_closed_eyes: :grinning: :kissing: :kissing_smiling_eyes: :stuck_out_tongue: :sleeping: :worried: :frowning: :anguished: :open_mouth: :grimacing: :confused: :hushed: :expressionless: :unamused: :sweat_smile: :sweat: :disappointed_relieved: :weary: :pensive: :disappointed: :confounded: :fearful: :cold_sweat: :persevere: :cry: :sob: :joy: :astonished: :scream: :neckbeard: :tired_face: :angry: :rage: :triumph: :sleepy: :yum: :mask: :sunglasses: :dizzy_face: :imp: :smiling_imp: :neutral_face: :no_mouth: :innocent: :alien: :yellow_heart: :blue_heart: :purple_heart: :heart: :green_heart: :broken_heart: :heartbeat: :heartpulse: :two_hearts: :revolving_hearts: :cupid: :sparkling_heart: :sparkles: :star: :star2: :dizzy: :boom: :collision: :anger: :exclamation: :question: :grey_exclamation: :grey_question: :zzz: :dash: :sweat_drops: :notes: :musical_note: :fire: :hankey: :poop: :shit: :+1: :thumbsup: :-1: :thumbsdown: :ok_hand: :punch: :facepunch: :fist: :v: :wave: :hand: :raised_hand: :open_hands: :point_up: :point_down: :point_left: :point_right: :raised_hands: :pray: :point_up_2: :clap: :muscle: :metal: :fu: :runner: :running: :couple: :family: :two_men_holding_hands: :two_women_holding_hands: :dancer: :dancers: :ok_woman: :no_good: :information_desk_person: :raising_hand: :bride_with_veil: :person_with_pouting_face: :person_frowning: :bow: :couplekiss: :couple_with_heart: :massage: :haircut: :nail_care: :boy: :girl: :woman: :man: :baby: :older_woman: :older_man: :person_with_blond_hair: :man_with_gua_pi_mao: :man_with_turban: :construction_worker: :cop: :angel: :princess: :smiley_cat: :smile_cat: :heart_eyes_cat: :kissing_cat: :smirk_cat: :scream_cat: :crying_cat_face: :joy_cat: :pouting_cat: :japanese_ogre: :japanese_goblin: :see_no_evil: :hear_no_evil: :speak_no_evil: :guardsman: :skull: :feet: :lips: :kiss: :droplet: :ear: :eyes: :nose: :tongue: :love_letter: :bust_in_silhouette: :busts_in_silhouette: :speech_balloon: :thought_balloon: :feelsgood: :finnadie: :goberserk: :godmode: :hurtrealbad: :rage1: :rage2: :rage3: :rage4: :suspect: :trollface: +:bowtie: :smile: :laughing: :blush: :smiley: :relaxed: :smirk: :heart_eyes: :kissing_heart: :kissing_closed_eyes: :flushed: :relieved: :satisfied: :grin: :wink: :stuck_out_tongue_winking_eye: :stuck_out_tongue_closed_eyes: :grinning: :kissing: :kissing_smiling_eyes: :stuck_out_tongue: :sleeping: :worried: :frowning: :anguished: :open_mouth: :grimacing: :confused: :hushed: :expressionless: :unamused: :sweat_smile: :sweat: :disappointed_relieved: :weary: :pensive: :disappointed: :confounded: :fearful: :cold_sweat: :persevere: :cry: :sob: :joy: :astonished: :scream: :neckbeard: :tired_face: :angry: :rage: :triumph: :sleepy: :yum: :mask: :sunglasses: :dizzy_face: :imp: :smiling_imp: :neutral_face: :no_mouth: :innocent: :alien: :yellow_heart: :blue_heart: :purple_heart: :heart: :green_heart: :broken_heart: :heartbeat: :heartpulse: :two_hearts: :revolving_hearts: :cupid: :sparkling_heart: :sparkles: :star: :star2: :dizzy: :boom: :collision: :anger: :exclamation: :question: :grey_exclamation: :grey_question: :zzz: :dash: :sweat_drops: :notes: :musical_note: :fire: :hankey: :poop: :shit: :+1: :thumbsup: :-1: :thumbsdown: :ok_hand: :punch: :facepunch: :fist: :v: :wave: :hand: :raised_hand: :open_hands: :point_up: :point_down: :point_left: :point_right: :raised_hands: :pray: :point_up_2: :clap: :muscle: :metal: :fu: :runner: :running: :couple: :family: :two_men_holding_hands: :two_women_holding_hands: :dancer: :dancers: :ok_woman: :no_good: :information_desk_person: :raising_hand: :bride_with_veil: :person_with_pouting_face: :person_frowning: :bow: :couplekiss: :couple_with_heart: :massage: :haircut: :nail_care: :boy: :girl: :woman: :man: :baby: :older_woman: :older_man: :person_with_blond_hair: :man_with_gua_pi_mao: :man_with_turban: :construction_worker: :cop: :angel: :princess: :smiley_cat: :smile_cat: :heart_eyes_cat: :kissing_cat: :smirk_cat: :scream_cat: :crying_cat_face: :joy_cat: :pouting_cat: :japanese_ogre: :japanese_goblin: :see_no_evil: :hear_no_evil: :speak_no_evil: :guardsman: :skull: :feet: :lips: :kiss: :droplet: :ear: :eyes: :nose: :tongue: :love_letter: :bust_in_silhouette: :busts_in_silhouette: :speech_balloon: :thought_balloon: :feelsgood: :finnadie: :goberserk: :godmode: :hurtrealbad: :rage1: :rage2: :rage3: :rage4: :suspect: :trollface: :slightly_smiling_face: :slightly_frowning_face: :upside_down_face: ### Emoticons - Nature :sunny: :umbrella: :cloud: :snowflake: :snowman: :zap: :cyclone: :foggy: :ocean: :cat: :dog: :mouse: :hamster: :rabbit: :wolf: :frog: :tiger: :koala: :bear: :pig: :pig_nose: :cow: :boar: :monkey_face: :monkey: :horse: :racehorse: :camel: :sheep: :elephant: :panda_face: :snake: :bird: :baby_chick: :hatched_chick: :hatching_chick: :chicken: :penguin: :turtle: :bug: :honeybee: :ant: :beetle: :snail: :octopus: :tropical_fish: :fish: :whale: :whale2: :dolphin: :cow2: :ram: :rat: :water_buffalo: :tiger2: :rabbit2: :dragon: :goat: :rooster: :dog2: :pig2: :mouse2: :ox: :dragon_face: :blowfish: :crocodile: :dromedary_camel: :leopard: :cat2: :poodle: :paw_prints: :bouquet: :cherry_blossom: :tulip: :four_leaf_clover: :rose: :sunflower: :hibiscus: :maple_leaf: :leaves: :fallen_leaf: :herb: :mushroom: :cactus: :palm_tree: :evergreen_tree: :deciduous_tree: :chestnut: :seedling: :blossom: :ear_of_rice: :shell: :globe_with_meridians: :sun_with_face: :full_moon_with_face: :new_moon_with_face: :new_moon: :waxing_crescent_moon: :first_quarter_moon: :waxing_gibbous_moon: :full_moon: :waning_gibbous_moon: :last_quarter_moon: :waning_crescent_moon: :last_quarter_moon_with_face: :first_quarter_moon_with_face: :crescent_moon: :earth_africa: :earth_americas: :earth_asia: :volcano: :milky_way: :partly_sunny: :octocat: :squirrel: diff --git a/tests/test-emoticons3.md b/tests/test-emoticons3.md index e160268241..17fd3f7a2c 100644 --- a/tests/test-emoticons3.md +++ b/tests/test-emoticons3.md @@ -1,3 +1,3 @@ ### Emoticons - Places -:house: :house_with_garden: :school: :office: :post_office: :hospital: :bank: :convenience_store: :love_hotel: :hotel: :wedding: :church: :department_store: :european_post_office: :city_sunrise: :city_sunset: :japanese_castle: :european_castle: :tent: :factory: :tokyo_tower: :japan: :mount_fuji: :sunrise_over_mountains: :sunrise: :stars: :statue_of_liberty: :bridge_at_night: :carousel_horse: :rainbow: :ferris_wheel: :fountain: :roller_coaster: :ship: :speedboat: :boat: :sailboat: :rowboat: :anchor: :rocket: :airplane: :helicopter: :steam_locomotive: :tram: :mountain_railway: :bike: :aerial_tramway: :suspension_railway: :mountain_cableway: :tractor: :blue_car: :oncoming_automobile: :car: :red_car: :taxi: :oncoming_taxi: :articulated_lorry: :bus: :oncoming_bus: :rotating_light: :police_car: :oncoming_police_car: :fire_engine: :ambulance: :minibus: :truck: :train: :station: :train2: :bullettrain_front: :bullettrain_side: :light_rail: :monorail: :railway_car: :trolleybus: :ticket: :fuelpump: :vertical_traffic_light: :traffic_light: :warning: :construction: :beginner: :atm: :slot_machine: :busstop: :barber: :hotsprings: :checkered_flag: :crossed_flags: :izakaya_lantern: :moyai: :circus_tent: :performing_arts: :round_pushpin: :triangular_flag_on_post: :jp: :kr: :cn: :us: :fr: :es: :it: :ru: :gb: :uk: :de: +:house: :house_with_garden: :school: :office: :post_office: :hospital: :bank: :convenience_store: :love_hotel: :hotel: :wedding: :church: :department_store: :european_post_office: :city_sunrise: :city_sunset: :japanese_castle: :european_castle: :tent: :factory: :tokyo_tower: :japan: :mount_fuji: :sunrise_over_mountains: :sunrise: :stars: :statue_of_liberty: :bridge_at_night: :carousel_horse: :rainbow: :ferris_wheel: :fountain: :roller_coaster: :ship: :speedboat: :boat: :sailboat: :rowboat: :anchor: :rocket: :airplane: :helicopter: :steam_locomotive: :tram: :mountain_railway: :bike: :aerial_tramway: :suspension_railway: :mountain_cableway: :tractor: :blue_car: :oncoming_automobile: :car: :red_car: :taxi: :oncoming_taxi: :articulated_lorry: :bus: :oncoming_bus: :rotating_light: :police_car: :oncoming_police_car: :fire_engine: :ambulance: :minibus: :truck: :train: :station: :train2: :bullettrain_front: :bullettrain_side: :light_rail: :monorail: :railway_car: :trolleybus: :ticket: :fuelpump: :vertical_traffic_light: :traffic_light: :warning: :construction: :beginner: :atm: :slot_machine: :busstop: :barber: :hotsprings: :checkered_flag: :crossed_flags: :izakaya_lantern: :moyai: :circus_tent: :performing_arts: :round_pushpin: :triangular_flag_on_post: :jp: :kr: :cn: :us: :fr: :es: :it: :ru: :gb: :uk: :de: :ca: :eh: :pk: :za: diff --git a/webapp/components/suggestion/emoticon_provider.jsx b/webapp/components/suggestion/emoticon_provider.jsx index bbf7c6f516..b7f4cd5136 100644 --- a/webapp/components/suggestion/emoticon_provider.jsx +++ b/webapp/components/suggestion/emoticon_provider.jsx @@ -55,7 +55,7 @@ export default class EmoticonProvider { const matched = []; - for (const [name, emoticon] of Emoticons.emoticons) { + for (const [name, emoticon] of Emoticons.getEmoticonsByName()) { if (name.indexOf(partialName) !== -1) { matched.push(emoticon); diff --git a/webapp/images/emoji/1f1ff-1e1e6.png b/webapp/images/emoji/1f1ff-1f1e6.png similarity index 100% rename from webapp/images/emoji/1f1ff-1e1e6.png rename to webapp/images/emoji/1f1ff-1f1e6.png diff --git a/webapp/images/emoji/mm.png b/webapp/images/emoji/mm.png new file mode 100644 index 0000000000000000000000000000000000000000..90930aabe1adac355c496f407bd31f1593a6bb76 GIT binary patch literal 6576 zcmbVR2|Uw%{GVI(6hiKZP-%9swapzlk~4`Avkl8^+F{O`dV1U*a-}GeNOB}RmAjmY ziV#T>iR{t1ONb=%}kAK zIj{OFPeFdpH4lIC73YPEVr#M=P|zkj%K4yxF}1Y-00LD20LGsHz#>P)cn1K`zyQFQ z8vua1002n%rropA<|sD!nmSSdfK8$+PcFd4G)a!E8gJ)7b+9;u!jOE_(O8l@PTk+f zmtze8X#1l$Z+&o7w1U46fk;95>nN^Tpg8YW#2`h5RTHYWj-rEwwSpmuj8lNAYpR14 zbvG&~C}@+h9w=KQ<8N@zCmlsED%BSS0@3Mob-IQ+iR=l2AdyHA7z%R`~XU^svLZ(u8$-@>q%?^XHI$b{9Juow`Ifb+o-sT2+l^2>0Xv$e4J4*ge}{%GIT za6Uf2L{g{+{5Yk5JIY@oDRu$AIFK!lLZXo|xC4HiOq70AGlgo4`%TenN&MRt$NZO$ zIMw{`8T~4{uNR3*qIi*h1O2Z4diJZF`r2ykl}4gg$|KNL7;%J%r|LqrSNr?3_yye21P3ELr<;^j3>2Xa`laS)q#=nwA|LieV{p1~ z7)%594Yp4FGh#KC<8NcF)2u^cF(?lb*#}M4#rvQ=aUfr!r#9$^aGm8UFJvUh}hR^?N6HB87@3VsPJ@ zgy%E~hQMO68W?w=JA~63O%F62h=jo)Ko3n#4G0_z)r3NkYu?wve;)&lSsexn+7E|a z1AdeJ4AKUz>|O%?-@W@P{dY^(X!2hQXn*C8+5fk)|GXc3pZ_CAIppCiOw_tAtW4TU6E$HP z`#dzk7$6v}i2!Q2b1q0U7z%_!IZ^};hI5B=_St{nKXUrY{T1)nA9MWm>zO0qMEJfv zQ5YBw4h6eIfk*@b3WPv#Fdzc1;Q_=#_aQlqfg*8GkF{9qoc)OP{cwI9z&Zk~96HK^ zb3cGZQ_)6fD(-(H{eNuNMOo1ql5rdlYhGYL2q$5%9R!ThgrGE$Yoqg%-_!b#Omtjq=(bxtkGYIzoEVk)Vf=q z)#Lsu<=jx|Zsc@i?dD8(eL)RP>&oIIxBBkq%l=*5a1;@J>^<8r` z5=;e0Xcgp5AR?hp_V@O*p$f*BbS##n9GFzsfG4dl&w zAgw#yq$FXPj+#v@E-p@3T3ocZ28D&pT-FJSy?VT7VBm|&Sl_NO>1%sZuaPJ7h6FnG z9JqFA=?;f0#X4yF^xmPIoGZ=HiV4(hQ_bj_$$7gtmYJD(yzM3ytlU?8X6umK@@U4z z3gy|y)-7`t0Us}$FQq4bJn73tBX>n!Rl4Cp;5Yau;88MAqAp=9&Aftd)3!7nFu2HX zs3As1`tAA9=FbpHg0ft9=HR(a61qH0(p9!oT9Cjjx2$aO6TgLHYI-oOYJMi@Dk#E0 z3wLly01kTC@OM~Pm}`PSsf|g(9OiZkBQSne=MnaZ1nVv zk3QRS*Wpd9dGlNvl45y1q%!*H)2EJII!y{%p3m)tlG>ga@njq}4)XuJywJ|y)9pKc z0F@%weOcdlFo#fk`p?hlGZTu|^Q68qpG_fVT1RS{;ce|0N=4F?lvz==-izBV*|{X#}deJ?P6?Iv{5`+-SOAetO`t7Jb~aoY)_zRPm4OF}@vtK7DUy znGATdoPDSN?t_3@Bs_np^ZxFXlQDWijnx;^Q%E+JZnW21w-qdT7kzB8b#rm)coN?^ zadq~6x6CzzGesy&RL!_*U-ktpAMT7Dk9NJ*-5&yE*wGc!%Qd*)8GNdKyQnLbqsKE5 zp!mZ z=J))r?PUcA`Nl3Bl_$$EM)Np;mkIk4IM(jrYbhB>^c$aaeYj9wLvL+OWdlr0;uB}# z_rzoduHJGvda%dJz%5n~VCW)duqXDJ)DTRnq--WL-u)^nly{4vsymeicaA;b{Bhr# z<)D&K-;B27NfNhtUdmOSOK4OOE^knG9I>SHTzH1+i5{Z`(+aezQ(o{xRCi2p&O=_>7LBq&;0BsdBH`}b!%&3Y(7FbDD<^$XyKU?u&}&NwEUcOHyv;_*_D1EWT9^skOgz(gutMU{6=9j3?F#;p%fdme z8yAPg^ABCU7m>i1oYdI&!t{ou{<9524}>%ZB~U8Q{E4wn*&*i=UQPA%L#d&12l=1mYIHhCGM5x|5$S^_&uv z<9VM(4A+gTR8ymlrtrM3X?jy1m0}&#b+jr?j(#d&!1MgAfV~@2dy2g}G@2|Nn_`gmwePn9k5M&4nCgTip18E(7fa6ii_k3dxKrnGEY&;N zb*4FepN>|>FZ(auknNeSh>3*Mq!lpWAG5VYy4mTi7zM>96%toa?ah+Mip4kuGpTwC z=8dfnRc9*Tt#iTxzBvgS2;qoDNw-SUd?IV2_MOCGptZ>Ml zR0QzNf%D$(x}Pi4F)ElRi7C^WaQbq#{|iJ0XeN_Y(G{pNd-Ha`;v&#z*T}|QL!;Rj zS)!9mx8n>VB%nD9=aZ|psD#yd32X5jV1Y>|rnx!2=aWN%ay;9~ug zTOzkShcilF@YLy(qub7=iiAwH&2OfSs1igNJ0D^}O{oRkn?1ic*|&W-e&k(%O3KxN z(b6wkN&ECdY1+3gU|ewZ*%r?7C7}i4afy;M4?9hHRdxIng>8HiAMR8r|zAanSc{vge=WT_4|+87ahtB z=022_lYJttW%}v1Nq|+%D;;~TgH-~F@EN1z>X@@kl8vzUCBQW^#LR`geMj;&B-NJH zr4L);(^bs*Ga!_vemEDIM%w~>^Q)A^j(H*~{NuW^>-KJExj0_<1OeFEWESeD_O_3LFbN z%}#_W@tvEZkgI_T^+_L?wvunygKGFf-lk&$Ma(cU9`~nDE2H}#s-N0u{)Ij>EY85s zE7tzKSQ_42_^joxzei*1WDRBD1aMD;UC~an%U!1;YovCeGDTC&q;TcM8!cN?cT*po z(dj)CQ8WAT-k5SL4O)?vdUWZ_8#K#v!6Ly!!2=5(;nI4`bwbc-rH*uO3`c;wVftPL0ylj54huYq| z-BVU9kMv{lpw0Uqm~F7SJc$C220OclDpj`RP^?}A2t3Nj3OU+!(5C6biD5Ey=#)QL z&~~GHPC(D+NXVed(v^mV6r6Kuit^CFqo&9y-(yuam+H*pC(u|8i*wYx2<@Dk6p;j1 zi(C2Kq!%jvhQ@;xg;OC>Y_3Vf=|Shqj?OBCa0^}TxL{hv9_=FU?gBYkr+zaloOA8* zEgR~18Xh6d+6L!i^=@08l(O{GFr+z0zWsFRT)7f^_`ES~IPmV`L6*!*Ka#O!KeyBK zyG@^H$os{qi7YxSz13-G*VD>Bot}Kq#y|dxIh;v+uA9%J$c*w1j(#+OLChx+Y;yA2 za+Z% z0o`vRH%q5f1sFDD65KK!GEBl}I*hAp6`rrvZddSK4eT6R;;T~6Ba?Tqr$k{j){ zZO(k7CeVvTY9n}BoM-Zq<08l=W~(mpxHU*g{fdb03GGdW)mJ|h!uSZ+d(Vq*@_(Xl zif}89nYLwm)$<)89IRG^*nVzvF-C=y_vApDb#_2s;U*sY2!B6dz0{QeE zxjtXZMyYul!L_vq~A%TnHlC6nFxIX6KvfN~X&<^Wcu$NJq zh??0YlY(J6$=H+~XHM|s5}X#AE-xkv9_*|Y>T7YjYE=?{hwGu(3-gno`4?kSh17=l z^pOU$qJP2&kyW|cQB{m*e|9G9m<0~HmBvn$mDRr?0fGnUd_rDVTFQp!ufe$ldDyHv zJ!aR4%nn!Y4bNgMC#{qtYS|A4cwFkP&*K-}y$^_PVDN9Qt90HX^mLBSds&1R{4_d~ zT=TYN#-0fq|BJpOmHxtHzpQ-9F_S!ObH>7Z8$JJm>z_1Gd18W%DwxCUvP4-E#Q5G< znd<5v=G2$$+q4Fajxtcv@>r`L{kx&&bwT4%A5T14Ff3K9JDGCdajq*s-0?K$r;x~q zH!hg{nyCOpsEwssZAj?hULVFqfeE7%p_1YMoO^MZmG@+{$!oD)baA?BdcT_|tom`@O&ZL8BT^UB+9y-~raxy%E)f~yNW7?}kl6&h9nM4zo4xJR3r4gv7 z_9Q62AG3(eQ&7S2DyLj4d$x5Z#Y0=XO1g+yrg-rHdQ3|rI#xt%AEqRcQlqTwcbMLa&Th6h zqrQ1;D9(UxWrioA%Q|qj+j8%0PD2$xyd@+h7YpbNC0(44Lpce&Jx%|E$Fq62%9EA9 OM$8Ua8Wrq!JM%vVNXZER literal 0 HcmV?d00001 diff --git a/webapp/package.json b/webapp/package.json index 1788c43f93..9c0377cdd9 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -29,7 +29,7 @@ "react-router": "2.0.1", "react-textarea-autosize": "3.3.0", "superagent": "1.8.3", - "twemoji": "1.4.1", + "twemoji": "2.0.5", "velocity-animate": "1.2.3" }, "devDependencies": { diff --git a/webapp/utils/emoji.json b/webapp/utils/emoji.json index c01f5b6795..8d6d7c7a6d 100644 --- a/webapp/utils/emoji.json +++ b/webapp/utils/emoji.json @@ -8181,6 +8181,14 @@ , "tags": [ ] } +, { + "aliases": [ + "mm", + "mattermost" + ] + , "tags": [ + ] + } , { "aliases": [ "basecamp" diff --git a/webapp/utils/emoticons.jsx b/webapp/utils/emoticons.jsx index 86f7a5b7b0..505e10c193 100644 --- a/webapp/utils/emoticons.jsx +++ b/webapp/utils/emoticons.jsx @@ -29,10 +29,12 @@ const emoticonPatterns = { thumbsdown: /(^|\s)(:\-1:)(?=$|\s)/g // :-1: }; -export const emoticons = initializeEmoticons(); +let emoticonsByName; +let emoticonsByCodePoint; function initializeEmoticons() { - const emoticonMap = new Map(); + emoticonsByName = new Map(); + emoticonsByCodePoint = new Set(); for (const emoji of emojis) { const unicode = emoji.emoji; @@ -40,6 +42,8 @@ function initializeEmoticons() { let filename = ''; if (unicode) { // this is a unicode emoji so the character code determines the file name + let codepoint = ''; + for (let i = 0; i < unicode.length; i += 2) { const code = fixedCharCodeAt(unicode, i); @@ -50,25 +54,26 @@ function initializeEmoticons() { // some emoji (such as country flags) span multiple unicode characters if (i !== 0) { - filename += '-'; + codepoint += '-'; } - filename += pad(code.toString(16)); + codepoint += pad(code.toString(16)); } + + filename = codepoint; + emoticonsByCodePoint.add(codepoint); } else { // this isn't a unicode emoji so the first alias determines the file name filename = emoji.aliases[0]; } for (const alias of emoji.aliases) { - emoticonMap.set(alias, { + emoticonsByName.set(alias, { alias, path: getImagePathForEmoticon(filename) }); } } - - return emoticonMap; } // Pads a hexadecimal number with zeroes to be at least 4 digits long @@ -110,14 +115,30 @@ function fixedCharCodeAt(str, idx = 0) { return code; } +export function getEmoticonsByName() { + if (!emoticonsByName) { + initializeEmoticons(); + } + + return emoticonsByName; +} + +export function getEmoticonsByCodePoint() { + if (!emoticonsByCodePoint) { + initializeEmoticons(); + } + + return emoticonsByCodePoint; +} + export function handleEmoticons(text, tokens) { let output = text; function replaceEmoticonWithToken(fullMatch, prefix, matchText, name) { - if (emoticons.has(name)) { + if (getEmoticonsByName().has(name)) { const index = tokens.size; const alias = `MM_EMOTICON${index}`; - const path = emoticons.get(name).path; + const path = getEmoticonsByName().get(name).path; tokens.set(alias, { value: `${matchText}`, @@ -141,6 +162,6 @@ export function handleEmoticons(text, tokens) { return output; } -function getImagePathForEmoticon(name) { +export function getImagePathForEmoticon(name) { return Constants.EMOJI_PATH + '/' + name + '.png'; } diff --git a/webapp/utils/text_formatting.jsx b/webapp/utils/text_formatting.jsx index cb61ecc8d7..96b51d6321 100644 --- a/webapp/utils/text_formatting.jsx +++ b/webapp/utils/text_formatting.jsx @@ -60,17 +60,25 @@ export function doFormatText(text, options) { output = highlightCurrentMentions(output, tokens); } - // reinsert tokens with formatted versions of the important words and phrases - output = replaceTokens(output, tokens); - if (!('emoticons' in options) || options.emoticon) { output = twemoji.parse(output, { className: 'emoticon', base: '', - folder: Constants.EMOJI_PATH + folder: Constants.EMOJI_PATH, + callback: (icon, twemojiOptions) => { + if (!Emoticons.getEmoticonsByCodePoint().has(icon)) { + // just leave the unicode characters and hope the browser can handle it + return null; + } + + return ''.concat(twemojiOptions.base, twemojiOptions.size, '/', icon, twemojiOptions.ext); + } }); } + // reinsert tokens with formatted versions of the important words and phrases + output = replaceTokens(output, tokens); + return output; }