diff --git a/packages/react/src/hooks/useSearchEmoji.js b/packages/react/src/hooks/useSearchEmoji.js
new file mode 100644
index 0000000000..7112fc3d81
--- /dev/null
+++ b/packages/react/src/hooks/useSearchEmoji.js
@@ -0,0 +1,67 @@
+import { useCallback } from 'react';
+import emojiList from '../lib/emojiList';
+
+const useSearchEmoji = (
+ startReadEmoji,
+ setStartReadEmoji,
+ setFilteredEmojis,
+ setEmojiIndex,
+ setShowEmojiList
+) =>
+ useCallback(
+ (message) => {
+ const lastChar = message ? message[message.length - 1] : '';
+
+ if (message.length === 0) {
+ setShowEmojiList(false);
+ setStartReadEmoji(false);
+ setFilteredEmojis([]);
+ setEmojiIndex(-1);
+ return;
+ }
+
+ // Check if user is typing emoji syntax (:emoji:)
+ const emojiMatch = message.match(/:([a-zA-Z0-9_+-]*?)$/);
+
+ if (emojiMatch) {
+ const query = emojiMatch[1].toLowerCase();
+
+ // Only show suggestions if query is at least 2 characters
+ if (query.length >= 2) {
+ setStartReadEmoji(true);
+
+ const filteredEmojis = emojiList
+ .filter(
+ (emoji) =>
+ emoji.shortname.toLowerCase().includes(query) ||
+ emoji.aliases.some((alias) =>
+ alias.toLowerCase().includes(query)
+ )
+ )
+ .slice(0, 10);
+
+ setFilteredEmojis(filteredEmojis);
+ setEmojiIndex(filteredEmojis.length > 0 ? 0 : -1);
+ setShowEmojiList(filteredEmojis.length > 0);
+ } else {
+ setShowEmojiList(false);
+ setFilteredEmojis([]);
+ setEmojiIndex(-1);
+ }
+ } else if (startReadEmoji) {
+ setStartReadEmoji(false);
+ setFilteredEmojis([]);
+ setEmojiIndex(-1);
+ setShowEmojiList(false);
+ }
+ },
+ [
+ startReadEmoji,
+ setStartReadEmoji,
+ setFilteredEmojis,
+ setEmojiIndex,
+ setShowEmojiList,
+ ]
+ );
+
+export default useSearchEmoji;
diff --git a/packages/react/src/lib/emojiList.js b/packages/react/src/lib/emojiList.js
new file mode 100644
index 0000000000..4aa794a7a0
--- /dev/null
+++ b/packages/react/src/lib/emojiList.js
@@ -0,0 +1,464 @@
+// Comprehensive list of popular emojis with emoji-toolkit compatible shortnames
+// Based on the most commonly used emojis and matching Rocket.Chat's emoji autocomplete
+
+const emojiList = [
+ // Popular smileys and emotions
+ { shortname: 'smile', emoji: '๐', aliases: ['happy', 'grinning'] },
+ { shortname: 'laughing', emoji: '๐', aliases: ['joy', 'tears'] },
+ { shortname: 'heart_eyes', emoji: '๐', aliases: ['love', 'heart'] },
+ { shortname: 'kissing_heart', emoji: '๐', aliases: ['kiss', 'love'] },
+ { shortname: 'wink', emoji: '๐', aliases: ['winking'] },
+ { shortname: 'blush', emoji: '๐', aliases: ['smiling', 'happy'] },
+ { shortname: 'grinning', emoji: '๐', aliases: ['grin', 'happy'] },
+ { shortname: 'grin', emoji: '๐', aliases: ['grinning', 'happy'] },
+ { shortname: 'joy', emoji: '๐', aliases: ['laughing', 'tears'] },
+ { shortname: 'smiley', emoji: '๐', aliases: ['happy', 'grinning'] },
+ { shortname: 'smirk', emoji: '๐', aliases: ['smirking'] },
+ { shortname: 'relaxed', emoji: 'โบ๏ธ', aliases: ['smile', 'happy'] },
+ {
+ shortname: 'stuck_out_tongue',
+ emoji: '๐',
+ aliases: ['tongue', 'playful'],
+ },
+ {
+ shortname: 'stuck_out_tongue_winking_eye',
+ emoji: '๐',
+ aliases: ['wink', 'tongue'],
+ },
+ {
+ shortname: 'stuck_out_tongue_closed_eyes',
+ emoji: '๐',
+ aliases: ['tongue', 'playful'],
+ },
+ { shortname: 'kissing', emoji: '๐', aliases: ['kiss'] },
+ {
+ shortname: 'kissing_smiling_eyes',
+ emoji: '๐',
+ aliases: ['kiss', 'happy'],
+ },
+ { shortname: 'kissing_closed_eyes', emoji: '๐', aliases: ['kiss', 'love'] },
+ { shortname: 'yum', emoji: '๐', aliases: ['delicious', 'tasty'] },
+ { shortname: 'relieved', emoji: '๐', aliases: ['relief', 'sigh'] },
+ { shortname: 'hugs', emoji: '๐ค', aliases: ['hugging'] },
+ { shortname: 'thinking', emoji: '๐ค', aliases: ['thought', 'considering'] },
+ { shortname: 'neutral_face', emoji: '๐', aliases: ['neutral', 'meh'] },
+ { shortname: 'expressionless', emoji: '๐', aliases: ['blank', 'neutral'] },
+ { shortname: 'no_mouth', emoji: '๐ถ', aliases: ['mute', 'silent'] },
+ { shortname: 'rolling_eyes', emoji: '๐', aliases: ['eyeroll', 'sarcasm'] },
+ { shortname: 'smiling_imp', emoji: '๐', aliases: ['devil', 'evil'] },
+ { shortname: 'imp', emoji: '๐ฟ', aliases: ['devil', 'evil'] },
+ { shortname: 'japanese_ogre', emoji: '๐น', aliases: ['monster', 'ogre'] },
+ { shortname: 'japanese_goblin', emoji: '๐บ', aliases: ['goblin', 'monster'] },
+ { shortname: 'skull', emoji: '๐', aliases: ['death', 'dead'] },
+ { shortname: 'ghost', emoji: '๐ป', aliases: ['spooky', 'halloween'] },
+ { shortname: 'alien', emoji: '๐ฝ', aliases: ['ufo', 'extraterrestrial'] },
+ { shortname: 'robot', emoji: '๐ค', aliases: ['bot', 'automation'] },
+ { shortname: 'smiley_cat', emoji: '๐ธ', aliases: ['cat', 'happy'] },
+ { shortname: 'smile_cat', emoji: '๐น', aliases: ['cat', 'laughing'] },
+ { shortname: 'joy_cat', emoji: '๐ป', aliases: ['cat', 'love'] },
+ { shortname: 'heart_eyes_cat', emoji: '๐ป', aliases: ['cat', 'love'] },
+ { shortname: 'smirk_cat', emoji: '๐ผ', aliases: ['cat', 'smirk'] },
+ { shortname: 'kissing_cat', emoji: '๐ฝ', aliases: ['cat', 'kiss'] },
+ { shortname: 'scream_cat', emoji: '๐', aliases: ['cat', 'scared'] },
+ { shortname: 'crying_cat_face', emoji: '๐ฟ', aliases: ['cat', 'cry', 'sad'] },
+ { shortname: 'pouting_cat', emoji: '๐พ', aliases: ['cat', 'angry'] },
+
+ // Hand gestures
+ { shortname: 'thumbsup', emoji: '๐', aliases: ['yes', 'good', 'approve'] },
+ {
+ shortname: 'thumbsdown',
+ emoji: '๐',
+ aliases: ['no', 'bad', 'disapprove'],
+ },
+ { shortname: 'clap', emoji: '๐', aliases: ['applause', 'bravo'] },
+ { shortname: 'open_hands', emoji: '๐', aliases: ['open', 'hands'] },
+ { shortname: 'palms_up_together', emoji: '๐คฒ', aliases: ['prayer', 'hands'] },
+ { shortname: 'handshake', emoji: '๐ค', aliases: ['deal', 'agreement'] },
+ { shortname: 'pray', emoji: '๐', aliases: ['please', 'thanks', 'hope'] },
+ { shortname: 'writing_hand', emoji: 'โ๏ธ', aliases: ['write', 'signature'] },
+ { shortname: 'nail_care', emoji: '๐
', aliases: ['nail', 'polish'] },
+ { shortname: 'selfie', emoji: '๐คณ', aliases: ['camera', 'photo'] },
+ { shortname: 'muscle', emoji: '๐ช', aliases: ['strong', 'flex', 'biceps'] },
+ { shortname: 'mechanical_arm', emoji: '๐ฆพ', aliases: ['arm', 'robot'] },
+ { shortname: 'mechanical_leg', emoji: '๐ฆฟ', aliases: ['leg', 'robot'] },
+ { shortname: 'leg', emoji: '๐ฆต', aliases: ['kick', 'limb'] },
+ { shortname: 'foot', emoji: '๐ฆถ', aliases: ['kick', 'limb'] },
+ { shortname: 'ear', emoji: '๐', aliases: ['listen', 'hear'] },
+ {
+ shortname: 'ear_with_hearing_aid',
+ emoji: '๐ฆป',
+ aliases: ['hearing', 'aid'],
+ },
+ { shortname: 'nose', emoji: '๐', aliases: ['smell', 'sniff'] },
+ { shortname: 'brain', emoji: '๐ง ', aliases: ['think', 'smart'] },
+ { shortname: 'anatomical_heart', emoji: '๐ซ', aliases: ['heart', 'health'] },
+ { shortname: 'lungs', emoji: '๐ซ', aliases: ['breath', 'health'] },
+ { shortname: 'tooth', emoji: '๐ฆท', aliases: ['dental', 'health'] },
+ { shortname: 'bone', emoji: '๐ฆด', aliases: ['skeleton', 'health'] },
+ { shortname: 'eyes', emoji: '๐', aliases: ['see', 'look', 'watch'] },
+ { shortname: 'eye', emoji: '๐๏ธ', aliases: ['see', 'look'] },
+ { shortname: 'tongue', emoji: '๐
', aliases: ['taste', 'lick'] },
+ { shortname: 'lips', emoji: '๐', aliases: ['kiss', 'mouth'] },
+
+ // Hearts and symbols
+ { shortname: 'heart', emoji: 'โค๏ธ', aliases: ['love', 'red'] },
+ { shortname: 'orange_heart', emoji: '๐งก', aliases: ['love', 'orange'] },
+ { shortname: 'yellow_heart', emoji: '๐', aliases: ['love', 'yellow'] },
+ { shortname: 'green_heart', emoji: '๐', aliases: ['love', 'green'] },
+ { shortname: 'blue_heart', emoji: '๐', aliases: ['love', 'blue'] },
+ { shortname: 'purple_heart', emoji: '๐', aliases: ['love', 'purple'] },
+ { shortname: 'black_heart', emoji: '๐ค', aliases: ['love', 'black'] },
+ { shortname: 'white_heart', emoji: '๐ค', aliases: ['love', 'white'] },
+ { shortname: 'brown_heart', emoji: '๐ค', aliases: ['love', 'brown'] },
+ { shortname: 'broken_heart', emoji: '๐', aliases: ['sad', 'broken'] },
+ {
+ shortname: 'heart_exclamation',
+ emoji: 'โฃ๏ธ',
+ aliases: ['love', 'exclamation'],
+ },
+ { shortname: 'two_hearts', emoji: '๐', aliases: ['love', 'two'] },
+ {
+ shortname: 'revolving_hearts',
+ emoji: '๐',
+ aliases: ['love', 'revolving'],
+ },
+ { shortname: 'heartbeat', emoji: '๐', aliases: ['love', 'beat'] },
+ { shortname: 'heartpulse', emoji: '๐', aliases: ['love', 'pulse'] },
+ { shortname: 'heart_eyes', emoji: '๐', aliases: ['love', 'heart'] },
+ { shortname: 'sparkling_heart', emoji: '๐', aliases: ['love', 'sparkle'] },
+ { shortname: 'cupid', emoji: '๐', aliases: ['love', 'arrow'] },
+ { shortname: 'gift_heart', emoji: '๐', aliases: ['love', 'gift'] },
+ {
+ shortname: 'heart_decoration',
+ emoji: '๐',
+ aliases: ['love', 'decoration'],
+ },
+ { shortname: 'peace_symbol', emoji: 'โฎ๏ธ', aliases: ['peace', 'symbol'] },
+ { shortname: 'latin_cross', emoji: 'โ๏ธ', aliases: ['cross', 'christian'] },
+ { shortname: 'star_and_crescent', emoji: 'โช๏ธ', aliases: ['islam', 'muslim'] },
+ { shortname: 'om', emoji: '๐๏ธ', aliases: ['hindu', 'om'] },
+ {
+ shortname: 'wheel_of_dharma',
+ emoji: 'โธ๏ธ',
+ aliases: ['buddhism', 'dharma'],
+ },
+ { shortname: 'star_of_david', emoji: 'โก๏ธ', aliases: ['judaism', 'star'] },
+ { shortname: 'six_pointed_star', emoji: '๐ฏ', aliases: ['star', 'jewish'] },
+ { shortname: 'menorah', emoji: '๐', aliases: ['hanukkah', 'candles'] },
+ { shortname: 'yin_yang', emoji: 'โฏ๏ธ', aliases: ['balance', 'tao'] },
+ { shortname: 'orthodox_cross', emoji: 'โฆ๏ธ', aliases: ['christian', 'cross'] },
+ {
+ shortname: 'place_of_worship',
+ emoji: '๐',
+ aliases: ['worship', 'religion'],
+ },
+ { shortname: 'ophiuchus', emoji: 'โ', aliases: ['snake', 'zodiac'] },
+ { shortname: 'aries', emoji: 'โ', aliases: ['ram', 'zodiac'] },
+ { shortname: 'taurus', emoji: 'โ', aliases: ['bull', 'zodiac'] },
+ { shortname: 'gemini', emoji: 'โ', aliases: ['twins', 'zodiac'] },
+ { shortname: 'cancer', emoji: 'โ', aliases: ['crab', 'zodiac'] },
+ { shortname: 'leo', emoji: 'โ', aliases: ['lion', 'zodiac'] },
+ { shortname: 'virgo', emoji: 'โ', aliases: ['maiden', 'zodiac'] },
+ { shortname: 'libra', emoji: 'โ', aliases: ['scales', 'zodiac'] },
+ { shortname: 'scorpius', emoji: 'โ', aliases: ['scorpion', 'zodiac'] },
+ { shortname: 'sagittarius', emoji: 'โ', aliases: ['archer', 'zodiac'] },
+ { shortname: 'capricorn', emoji: 'โ', aliases: ['goat', 'zodiac'] },
+ { shortname: 'aquarius', emoji: 'โ', aliases: ['water', 'zodiac'] },
+ { shortname: 'pisces', emoji: 'โ', aliases: ['fish', 'zodiac'] },
+ { shortname: 'id', emoji: '๐', aliases: ['identification', 'id'] },
+ { shortname: 'atom_symbol', emoji: 'โ๏ธ', aliases: ['atom', 'science'] },
+ { shortname: 'u7a7a', emoji: '๐ณ', aliases: ['japanese', 'empty'] },
+ { shortname: 'u5272', emoji: '๐น', aliases: ['japanese', 'discount'] },
+ {
+ shortname: 'radioactive',
+ emoji: 'โข๏ธ',
+ aliases: ['nuclear', 'radioactive'],
+ },
+ { shortname: 'biohazard', emoji: 'โฃ๏ธ', aliases: ['danger', 'biohazard'] },
+ { shortname: 'mobile_phone_off', emoji: '๐ด', aliases: ['off', 'phone'] },
+ { shortname: 'vibration_mode', emoji: '๐ณ', aliases: ['vibrate', 'phone'] },
+ { shortname: 'u6709', emoji: '๐ถ', aliases: ['japanese', 'available'] },
+ { shortname: 'u7121', emoji: '๐', aliases: ['japanese', 'not'] },
+ { shortname: 'u7533', emoji: '๐ธ', aliases: ['japanese', 'application'] },
+ { shortname: 'u55b6', emoji: '๐บ', aliases: ['japanese', 'open'] },
+ { shortname: 'u6708', emoji: '๐ท๏ธ', aliases: ['japanese', 'monthly'] },
+ {
+ shortname: 'eight_pointed_black_star',
+ emoji: 'โด๏ธ',
+ aliases: ['star', 'eight'],
+ },
+ { shortname: 'vs', emoji: '๐', aliases: ['versus', 'vs'] },
+ { shortname: 'accept', emoji: '๐', aliases: ['japanese', 'acceptable'] },
+ { shortname: 'white_flower', emoji: '๐ฎ', aliases: ['flower', 'white'] },
+ {
+ shortname: 'ideograph_advantage',
+ emoji: '๐',
+ aliases: ['japanese', 'advantage'],
+ },
+ { shortname: 'secret', emoji: 'ใ๏ธ', aliases: ['japanese', 'secret'] },
+ {
+ shortname: 'congratulations',
+ emoji: 'ใ๏ธ',
+ aliases: ['japanese', 'congratulations'],
+ },
+ { shortname: 'u5408', emoji: '๐ด', aliases: ['japanese', 'congratulation'] },
+ { shortname: 'u6e80', emoji: '๐ต', aliases: ['japanese', 'full'] },
+ { shortname: 'u7981', emoji: '๐ฒ', aliases: ['japanese', 'prohibited'] },
+ { shortname: 'a', emoji: '๐
ฐ๏ธ', aliases: ['blood', 'type'] },
+ { shortname: 'b', emoji: '๐
ฑ๏ธ', aliases: ['blood', 'type'] },
+ { shortname: 'ab', emoji: '๐', aliases: ['blood', 'type'] },
+ { shortname: 'cl', emoji: '๐', aliases: ['clear', 'clear'] },
+ { shortname: 'o2', emoji: '๐
พ๏ธ', aliases: ['blood', 'type'] },
+ { shortname: 'sos', emoji: '๐', aliases: ['help', 'emergency'] },
+ { shortname: 'no_entry', emoji: 'โ', aliases: ['no', 'entry'] },
+ { shortname: 'name_badge', emoji: '๐', aliases: ['name', 'badge'] },
+ { shortname: 'no_entry_sign', emoji: '๐ซ', aliases: ['no', 'entry'] },
+ { shortname: 'x', emoji: 'โ', aliases: ['no', 'cross'] },
+ { shortname: 'o', emoji: 'โญ', aliases: ['yes', 'circle'] },
+ { shortname: 'stop_sign', emoji: '๐', aliases: ['stop', 'sign'] },
+ { shortname: 'anger', emoji: '๐ข', aliases: ['angry', 'mad'] },
+ { shortname: 'hotsprings', emoji: 'โจ๏ธ', aliases: ['hot', 'springs'] },
+ { shortname: 'no_pedestrians', emoji: '๐ท', aliases: ['no', 'pedestrians'] },
+ { shortname: 'do_not_litter', emoji: '๐ฏ', aliases: ['no', 'litter'] },
+ { shortname: 'no_bicycles', emoji: '๐ณ', aliases: ['no', 'bicycles'] },
+ { shortname: 'non-potable_water', emoji: '๐ฑ', aliases: ['no', 'water'] },
+ { shortname: 'underage', emoji: '๐', aliases: ['underage', '18'] },
+ { shortname: 'no_mobile_phones', emoji: '๐ต', aliases: ['no', 'phone'] },
+ {
+ shortname: 'exclamation',
+ emoji: 'โ',
+ aliases: ['exclamation', 'warning'],
+ },
+ {
+ shortname: 'grey_exclamation',
+ emoji: 'โ',
+ aliases: ['exclamation', 'grey'],
+ },
+ { shortname: 'question', emoji: 'โ', aliases: ['question', 'help'] },
+ { shortname: 'grey_question', emoji: 'โ', aliases: ['question', 'grey'] },
+ { shortname: 'bangbang', emoji: 'โผ๏ธ', aliases: ['exclamation', 'double'] },
+ {
+ shortname: 'interrobang',
+ emoji: 'โ๏ธ',
+ aliases: ['exclamation', 'question'],
+ },
+ { shortname: '100', emoji: '๐ฏ', aliases: ['hundred', 'perfect'] },
+ { shortname: 'low_brightness', emoji: '๐
', aliases: ['dim', 'brightness'] },
+ {
+ shortname: 'high_brightness',
+ emoji: '๐',
+ aliases: ['bright', 'brightness'],
+ },
+ { shortname: 'trident', emoji: '๐ฑ', aliases: ['trident', 'fork'] },
+ { shortname: 'fleur_de_lis', emoji: 'โ๏ธ', aliases: ['fleur', 'lily'] },
+ {
+ shortname: 'part_alternation_mark',
+ emoji: 'ใฝ๏ธ',
+ aliases: ['japanese', 'part'],
+ },
+ { shortname: 'warning', emoji: 'โ ๏ธ', aliases: ['warning', 'caution'] },
+ {
+ shortname: 'children_crossing',
+ emoji: '๐ธ',
+ aliases: ['children', 'crossing'],
+ },
+ { shortname: 'beginner', emoji: '๐ฐ', aliases: ['beginner', 'leaf'] },
+ { shortname: 'recycle', emoji: 'โป๏ธ', aliases: ['recycle', 'green'] },
+ { shortname: 'u6307', emoji: '๐ฏ', aliases: ['japanese', 'point'] },
+ { shortname: 'chart', emoji: '๐น', aliases: ['chart', 'yen'] },
+ { shortname: 'sparkle', emoji: 'โจ', aliases: ['sparkle', 'shine'] },
+ {
+ shortname: 'eight_spoked_asterisk',
+ emoji: 'โณ๏ธ',
+ aliases: ['asterisk', 'eight'],
+ },
+ {
+ shortname: 'negative_squared_cross_mark',
+ emoji: 'โ',
+ aliases: ['no', 'cross'],
+ },
+ { shortname: 'white_check_mark', emoji: 'โ
', aliases: ['yes', 'check'] },
+ {
+ shortname: 'diamond_shape_with_a_dot_inside',
+ emoji: '๐ ',
+ aliases: ['diamond', 'dot'],
+ },
+ { shortname: 'cyclone', emoji: '๐', aliases: ['cyclone', 'spiral'] },
+ { shortname: 'loop', emoji: 'โฟ', aliases: ['loop', 'curly'] },
+ {
+ shortname: 'globe_with_meridians',
+ emoji: '๐',
+ aliases: ['globe', 'world'],
+ },
+ { shortname: 'm', emoji: 'โ๏ธ', aliases: ['metro', 'm'] },
+ { shortname: 'atm', emoji: '๐ง', aliases: ['atm', 'cash'] },
+ { shortname: 'sa', emoji: '๐๏ธ', aliases: ['japanese', 'service'] },
+ {
+ shortname: 'passport_control',
+ emoji: '๐',
+ aliases: ['passport', 'control'],
+ },
+ { shortname: 'customs', emoji: '๐', aliases: ['customs', 'border'] },
+ { shortname: 'baggage_claim', emoji: '๐', aliases: ['baggage', 'claim'] },
+ { shortname: 'left_luggage', emoji: '๐
', aliases: ['left', 'luggage'] },
+ {
+ shortname: 'wheelchair',
+ emoji: 'โฟ',
+ aliases: ['wheelchair', 'accessibility'],
+ },
+ { shortname: 'no_smoking', emoji: '๐ญ', aliases: ['no', 'smoking'] },
+ { shortname: 'wc', emoji: '๐พ', aliases: ['toilet', 'wc'] },
+ { shortname: 'parking', emoji: '๐
ฟ๏ธ', aliases: ['parking', 'p'] },
+ { shortname: 'potable_water', emoji: '๐ฐ', aliases: ['water', 'drinking'] },
+ { shortname: 'mens', emoji: '๐น', aliases: ['men', 'male'] },
+ { shortname: 'womens', emoji: '๐บ', aliases: ['women', 'female'] },
+ { shortname: 'baby_symbol', emoji: '๐ผ', aliases: ['baby', 'symbol'] },
+ { shortname: 'restroom', emoji: '๐ป', aliases: ['restroom', 'toilet'] },
+ {
+ shortname: 'put_litter_in_its_place',
+ emoji: '๐ฎ',
+ aliases: ['litter', 'bin'],
+ },
+ { shortname: 'cinema', emoji: '๐ฆ', aliases: ['cinema', 'movie'] },
+ {
+ shortname: 'signal_strength',
+ emoji: '๐ถ',
+ aliases: ['signal', 'strength'],
+ },
+ { shortname: 'koko', emoji: '๐', aliases: ['japanese', 'here'] },
+ { shortname: 'ng', emoji: '๐', aliases: ['ng', 'no'] },
+ { shortname: 'ok', emoji: '๐', aliases: ['ok', 'okay'] },
+ { shortname: 'up', emoji: '๐', aliases: ['up', 'arrow'] },
+ { shortname: 'cool', emoji: '๐', aliases: ['cool', 'fresh'] },
+ { shortname: 'new', emoji: '๐', aliases: ['new', 'fresh'] },
+ { shortname: 'free', emoji: '๐', aliases: ['free', 'complimentary'] },
+ { shortname: 'zero', emoji: '0๏ธโฃ', aliases: ['zero', '0'] },
+ { shortname: 'one', emoji: '1๏ธโฃ', aliases: ['one', '1'] },
+ { shortname: 'two', emoji: '2๏ธโฃ', aliases: ['two', '2'] },
+ { shortname: 'three', emoji: '3๏ธโฃ', aliases: ['three', '3'] },
+ { shortname: 'four', emoji: '4๏ธโฃ', aliases: ['four', '4'] },
+ { shortname: 'five', emoji: '5๏ธโฃ', aliases: ['five', '5'] },
+ { shortname: 'six', emoji: '6๏ธโฃ', aliases: ['six', '6'] },
+ { shortname: 'seven', emoji: '7๏ธโฃ', aliases: ['seven', '7'] },
+ { shortname: 'eight', emoji: '8๏ธโฃ', aliases: ['eight', '8'] },
+ { shortname: 'nine', emoji: '9๏ธโฃ', aliases: ['nine', '9'] },
+ { shortname: 'keycap_ten', emoji: '๐', aliases: ['ten', '10'] },
+
+ // Additional popular emojis
+ { shortname: 'fire', emoji: '๐ฅ', aliases: ['hot', 'flame'] },
+ { shortname: 'boom', emoji: '๐ฅ', aliases: ['explosion', 'bang'] },
+ { shortname: 'collision', emoji: '๐ฅ', aliases: ['explosion', 'bang'] },
+ { shortname: 'sweat_drops', emoji: '๐ฆ', aliases: ['water', 'drops'] },
+ { shortname: 'droplet', emoji: '๐ง', aliases: ['water', 'drop'] },
+ { shortname: 'dash', emoji: '๐จ', aliases: ['wind', 'fast'] },
+ { shortname: 'hole', emoji: '๐ณ๏ธ', aliases: ['hole', 'dark'] },
+ { shortname: 'bomb', emoji: '๐ฃ', aliases: ['bomb', 'explosive'] },
+ { shortname: 'speech_balloon', emoji: '๐ฌ', aliases: ['speech', 'bubble'] },
+ { shortname: 'eye_speech_bubble', emoji: '๐๏ธโ๐จ๏ธ', aliases: ['eye', 'speech'] },
+ { shortname: 'left_speech_bubble', emoji: '๐จ๏ธ', aliases: ['left', 'speech'] },
+ { shortname: 'right_anger_bubble', emoji: '๐ฏ๏ธ', aliases: ['right', 'anger'] },
+ { shortname: 'thought_balloon', emoji: '๐ญ', aliases: ['thought', 'bubble'] },
+ { shortname: 'zzz', emoji: '๐ค', aliases: ['sleep', 'tired'] },
+ { shortname: 'wave', emoji: '๐', aliases: ['hello', 'goodbye'] },
+ { shortname: 'raised_back_of_hand', emoji: '๐ค', aliases: ['stop', 'hand'] },
+ {
+ shortname: 'raised_hand_with_fingers_splayed',
+ emoji: '๐๏ธ',
+ aliases: ['stop', 'hand'],
+ },
+ { shortname: 'hand', emoji: 'โ', aliases: ['stop', 'hand'] },
+ { shortname: 'spock-hand', emoji: '๐', aliases: ['spock', 'vulcan'] },
+ { shortname: 'the_horns', emoji: '๐ค', aliases: ['rock', 'metal'] },
+ { shortname: 'call_me_hand', emoji: '๐ค', aliases: ['call', 'phone'] },
+ { shortname: 'point_left', emoji: '๐', aliases: ['left', 'point'] },
+ { shortname: 'point_right', emoji: '๐', aliases: ['right', 'point'] },
+ { shortname: 'point_up_2', emoji: '๐', aliases: ['up', 'point'] },
+ { shortname: 'middle_finger', emoji: '๐', aliases: ['middle', 'finger'] },
+ { shortname: 'point_down', emoji: '๐', aliases: ['down', 'point'] },
+ { shortname: 'point_up', emoji: 'โ๏ธ', aliases: ['up', 'point'] },
+ { shortname: 'point_up_2', emoji: '๐', aliases: ['up', 'point'] },
+ { shortname: 'crossed_fingers', emoji: '๐ค', aliases: ['luck', 'cross'] },
+ { shortname: 'love_you_gesture', emoji: '๐ค', aliases: ['love', 'you'] },
+ { shortname: 'metal', emoji: '๐ค', aliases: ['rock', 'metal'] },
+ { shortname: 'vulcan_salute', emoji: '๐', aliases: ['spock', 'vulcan'] },
+ {
+ shortname: 'index_pointing_at_the_viewer',
+ emoji: '๐ซต',
+ aliases: ['point', 'you'],
+ },
+ { shortname: 'palms_up_together', emoji: '๐คฒ', aliases: ['prayer', 'hands'] },
+ { shortname: 'back_of_hand', emoji: '๐ค', aliases: ['back', 'hand'] },
+ {
+ shortname: 'hand_with_index_finger_and_thumb_crossed',
+ emoji: '๐ค',
+ aliases: ['pinch', 'money'],
+ },
+ { shortname: 'index_pointing_up', emoji: 'โ๏ธ', aliases: ['up', 'point'] },
+ {
+ shortname: 'index_pointing_at_the_viewer_dark_skin_tone',
+ emoji: '๐ซต๐ฟ',
+ aliases: ['point', 'you', 'dark'],
+ },
+ {
+ shortname: 'index_pointing_at_the_viewer_light_skin_tone',
+ emoji: '๐ซต๐ป',
+ aliases: ['point', 'you', 'light'],
+ },
+ {
+ shortname: 'index_pointing_at_the_viewer_medium_dark_skin_tone',
+ emoji: '๐ซต๐พ',
+ aliases: ['point', 'you', 'medium_dark'],
+ },
+ {
+ shortname: 'index_pointing_at_the_viewer_medium_light_skin_tone',
+ emoji: '๐ซต๐ผ',
+ aliases: ['point', 'you', 'medium_light'],
+ },
+ {
+ shortname: 'index_pointing_at_the_viewer_medium_skin_tone',
+ emoji: '๐ซต๐ฝ',
+ aliases: ['point', 'you', 'medium'],
+ },
+ { shortname: 'thumbs_up', emoji: '๐', aliases: ['yes', 'good', 'approve'] },
+ {
+ shortname: 'thumbs_down',
+ emoji: '๐',
+ aliases: ['no', 'bad', 'disapprove'],
+ },
+ { shortname: 'raised_fist', emoji: 'โ', aliases: ['fist', 'punch'] },
+ { shortname: 'oncoming_fist', emoji: '๐', aliases: ['fist', 'punch'] },
+ { shortname: 'left_facing_fist', emoji: '๐ค', aliases: ['fist', 'left'] },
+ { shortname: 'right_facing_fist', emoji: '๐ค', aliases: ['fist', 'right'] },
+ { shortname: 'clap', emoji: '๐', aliases: ['applause', 'bravo'] },
+ { shortname: 'raised_hands', emoji: '๐', aliases: ['celebration', 'hands'] },
+ { shortname: 'open_hands', emoji: '๐', aliases: ['open', 'hands'] },
+ { shortname: 'palms_up_together', emoji: '๐คฒ', aliases: ['prayer', 'hands'] },
+ { shortname: 'handshake', emoji: '๐ค', aliases: ['deal', 'agreement'] },
+ { shortname: 'pray', emoji: '๐', aliases: ['please', 'thanks', 'hope'] },
+ { shortname: 'writing_hand', emoji: 'โ๏ธ', aliases: ['write', 'signature'] },
+ { shortname: 'nail_care', emoji: '๐
', aliases: ['nail', 'polish'] },
+ { shortname: 'selfie', emoji: '๐คณ', aliases: ['camera', 'photo'] },
+ { shortname: 'muscle', emoji: '๐ช', aliases: ['strong', 'flex', 'biceps'] },
+ { shortname: 'mechanical_arm', emoji: '๐ฆพ', aliases: ['arm', 'robot'] },
+ { shortname: 'mechanical_leg', emoji: '๐ฆฟ', aliases: ['leg', 'robot'] },
+ { shortname: 'leg', emoji: '๐ฆต', aliases: ['kick', 'limb'] },
+ { shortname: 'foot', emoji: '๐ฆถ', aliases: ['kick', 'limb'] },
+ { shortname: 'ear', emoji: '๐', aliases: ['listen', 'hear'] },
+ {
+ shortname: 'ear_with_hearing_aid',
+ emoji: '๐ฆป',
+ aliases: ['hearing', 'aid'],
+ },
+ { shortname: 'nose', emoji: '๐', aliases: ['smell', 'sniff'] },
+ { shortname: 'brain', emoji: '๐ง ', aliases: ['think', 'smart'] },
+ { shortname: 'anatomical_heart', emoji: '๐ซ', aliases: ['heart', 'health'] },
+ { shortname: 'lungs', emoji: '๐ซ', aliases: ['breath', 'health'] },
+ { shortname: 'tooth', emoji: '๐ฆท', aliases: ['dental', 'health'] },
+ { shortname: 'bone', emoji: '๐ฆด', aliases: ['skeleton', 'health'] },
+ { shortname: 'eyes', emoji: '๐', aliases: ['see', 'look', 'watch'] },
+ { shortname: 'eye', emoji: '๐๏ธ', aliases: ['see', 'look'] },
+ { shortname: 'tongue', emoji: '๐
', aliases: ['taste', 'lick'] },
+ { shortname: 'lips', emoji: '๐', aliases: ['kiss', 'mouth'] },
+];
+
+export default emojiList;
diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js
index ca748520a6..cc0fc49d9f 100644
--- a/packages/react/src/views/ChatInput/ChatInput.js
+++ b/packages/react/src/views/ChatInput/ChatInput.js
@@ -26,12 +26,14 @@ import MembersList from '../Mentions/MembersList';
import { TypingUsers } from '../TypingUsers';
import createPendingMessage from '../../lib/createPendingMessage';
import { CommandsList } from '../CommandList';
+import { EmojiList } from '../EmojiList';
import useSettingsStore from '../../store/settingsStore';
import ChannelState from '../ChannelState/ChannelState';
import QuoteMessage from '../QuoteMessage/QuoteMessage';
import { getChatInputStyles } from './ChatInput.styles';
import useShowCommands from '../../hooks/useShowCommands';
import useSearchMentionUser from '../../hooks/useSearchMentionUser';
+import useSearchEmoji from '../../hooks/useSearchEmoji';
import formatSelection from '../../lib/formatSelection';
import { parseEmoji } from '../../lib/emoji';
@@ -56,6 +58,10 @@ const ChatInput = ({ scrollToBottom }) => {
const [showMembersList, setShowMembersList] = useState(false);
const [showCommandList, setShowCommandList] = useState(false);
const [filteredCommands, setFilteredCommands] = useState([]);
+ const [showEmojiList, setShowEmojiList] = useState(false);
+ const [filteredEmojis, setFilteredEmojis] = useState([]);
+ const [emojiIndex, setEmojiIndex] = useState(-1);
+ const [startReadEmoji, setStartReadEmoji] = useState(false);
const [isMsgLong, setIsMsgLong] = useState(false);
const {
@@ -141,6 +147,14 @@ const ChatInput = ({ scrollToBottom }) => {
setShowMembersList
);
+ const searchEmoji = useSearchEmoji(
+ startReadEmoji,
+ setStartReadEmoji,
+ setFilteredEmojis,
+ setEmojiIndex,
+ setShowEmojiList
+ );
+
useEffect(() => {
RCInstance.auth.onAuthChange((user) => {
if (user) {
@@ -396,12 +410,17 @@ const ChatInput = ({ scrollToBottom }) => {
const onTextChange = (e, val) => {
sendTypingStart();
const message = val || e.target.value;
- messageRef.current.value = parseEmoji(message);
+
+ // Don't parse emojis if user is currently typing emoji autocomplete
+ const shouldParseEmoji = !message.match(/:([a-zA-Z0-9_+-]*?)$/);
+ messageRef.current.value = shouldParseEmoji ? parseEmoji(message) : message;
+
setDisableButton(!messageRef.current.value.length);
if (e !== null) {
handleNewLine(e, false);
searchMentionUser(message);
showCommands(e);
+ searchEmoji(message);
}
};
@@ -444,7 +463,7 @@ const ChatInput = ({ scrollToBottom }) => {
case e.code === 'Enter':
e.preventDefault();
- if (!showCommandList && !showMembersList) {
+ if (!showCommandList && !showMembersList && !showEmojiList) {
sendTypingStop();
sendMessage();
}
@@ -567,6 +586,18 @@ const ChatInput = ({ scrollToBottom }) => {
/>
)}
+ {showEmojiList && (
+
+ )}
+
{
+ const currentMessage = messageRef.current.value;
+ const emojiMatch = currentMessage.match(/:(.*?)$/);
+
+ if (emojiMatch) {
+ // Replace the :query with the selected emoji
+ const beforeQuery = currentMessage.substring(
+ 0,
+ currentMessage.lastIndexOf(':')
+ );
+ const insertionText = `${beforeQuery}${selectedEmoji.emoji} `;
+
+ messageRef.current.value = insertionText;
+
+ // Set cursor position after the emoji and space
+ const cursorPosition = insertionText.length;
+ messageRef.current.setSelectionRange(cursorPosition, cursorPosition);
+ messageRef.current.focus();
+
+ // Clear emoji autocomplete state
+ setFilteredEmojis([]);
+ setEmojiIndex(-1);
+ setStartReadEmoji(false);
+ setShowEmojiList(false);
+ }
+ },
+ [
+ messageRef,
+ setFilteredEmojis,
+ setEmojiIndex,
+ setShowEmojiList,
+ setStartReadEmoji,
+ ]
+ );
+
+ const setItemRef = (el, index) => {
+ itemRefs.current[index] = el;
+ };
+
+ useEffect(() => {
+ const handleKeyPress = (event) => {
+ switch (event.key) {
+ case 'Enter': {
+ const selectedEmoji = filteredEmojis[emojiIndex];
+ if (selectedEmoji) {
+ handleEmojiClick(selectedEmoji);
+ }
+ break;
+ }
+ case 'Escape': {
+ // Cancel emoji selection
+ setFilteredEmojis([]);
+ setEmojiIndex(-1);
+ setStartReadEmoji(false);
+ setShowEmojiList(false);
+ messageRef.current.focus();
+ break;
+ }
+ case 'ArrowUp':
+ event.preventDefault();
+ setEmojiIndex(
+ emojiIndex - 1 < 0 ? filteredEmojis.length - 1 : emojiIndex - 1
+ );
+ break;
+ case 'ArrowDown':
+ event.preventDefault();
+ setEmojiIndex(
+ emojiIndex + 1 >= filteredEmojis.length ? 0 : emojiIndex + 1
+ );
+ break;
+ default:
+ break;
+ }
+ };
+
+ document.addEventListener('keydown', handleKeyPress);
+
+ return () => {
+ document.removeEventListener('keydown', handleKeyPress);
+ };
+ }, [
+ emojiIndex,
+ filteredEmojis,
+ handleEmojiClick,
+ setEmojiIndex,
+ setFilteredEmojis,
+ setStartReadEmoji,
+ setShowEmojiList,
+ messageRef,
+ ]);
+
+ useEffect(() => {
+ if (itemRefs.current[emojiIndex]) {
+ itemRefs.current[emojiIndex].scrollIntoView({
+ block: 'nearest',
+ });
+ }
+ }, [emojiIndex]);
+
+ if (!filteredEmojis || filteredEmojis.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+ {filteredEmojis.map((emoji, index) => (
+ - handleEmojiClick(emoji)}
+ ref={(el) => setItemRef(el, index)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ handleEmojiClick(emoji);
+ }
+ }}
+ style={{
+ backgroundColor: index === emojiIndex && 'rgba(0, 0, 0, 0.1)',
+ color: index === emojiIndex && theme.colors.foreground,
+ }}
+ >
+ {emoji.emoji}
+ :{emoji.shortname}:
+
+ ))}
+
+
+ );
+}
+
+EmojiList.propTypes = {
+ emojiIndex: PropTypes.number,
+ messageRef: PropTypes.object.isRequired,
+ filteredEmojis: PropTypes.array,
+ setFilteredEmojis: PropTypes.func.isRequired,
+ setEmojiIndex: PropTypes.func.isRequired,
+ setStartReadEmoji: PropTypes.func.isRequired,
+ setShowEmojiList: PropTypes.func.isRequired,
+};
+
+export default EmojiList;
diff --git a/packages/react/src/views/EmojiList/EmojiList.styles.js b/packages/react/src/views/EmojiList/EmojiList.styles.js
new file mode 100644
index 0000000000..098e0a2bd4
--- /dev/null
+++ b/packages/react/src/views/EmojiList/EmojiList.styles.js
@@ -0,0 +1,53 @@
+import { css } from '@emotion/react';
+
+const getEmojiListStyles = (theme) => {
+ const styles = {
+ main: css`
+ margin: 0.2rem 2rem;
+ display: block;
+ max-height: 10rem;
+ overflow-y: auto;
+ overflow-x: hidden;
+ border: 1px solid ${theme.colors.border};
+ border-radius: 0.2rem;
+ background-color: ${theme.colors.background};
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ z-index: 1000;
+ position: relative;
+ `,
+
+ listItem: css`
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ padding: 0.5rem 0.75rem;
+ font-size: 14px;
+ border-bottom: 1px solid ${theme.colors.border};
+ transition: background-color 0.2s ease;
+
+ &:hover {
+ background-color: ${theme.colors.secondary};
+ }
+
+ &:last-child {
+ border-bottom: none;
+ }
+ `,
+
+ emoji: css`
+ font-size: 16px;
+ margin-right: 0.5rem;
+ flex-shrink: 0;
+ `,
+
+ shortname: css`
+ font-family: monospace;
+ color: ${theme.colors.mutedForeground};
+ font-size: 12px;
+ `,
+ };
+
+ return styles;
+};
+
+export default getEmojiListStyles;
diff --git a/packages/react/src/views/EmojiList/index.js b/packages/react/src/views/EmojiList/index.js
new file mode 100644
index 0000000000..794f574143
--- /dev/null
+++ b/packages/react/src/views/EmojiList/index.js
@@ -0,0 +1 @@
+export { default as EmojiList } from './EmojiList';