diff --git a/app/Devnull/ReactionEmoji.php b/app/Devnull/ReactionEmoji.php new file mode 100644 index 0000000..7594fb7 --- /dev/null +++ b/app/Devnull/ReactionEmoji.php @@ -0,0 +1,60 @@ +|null + */ + private static ?array $byKey = null; + + /** + * What devnull takes for the emoji: itself, or the same emoji without its skin tone or with devnull's variation + * selector. Null when devnull has nothing like it. + */ + public static function forDevnull(string $emoji): ?string + { + return self::byKey()[self::key($emoji)] ?? null; + } + + /** + * The emoji with no skin tone and no variation selector, which no two of devnull's share. + */ + private static function key(string $emoji): string + { + return (string) preg_replace('/[\x{FE0F}\x{1F3FB}-\x{1F3FF}]/u', '', trim($emoji)); + } + + /** + * @return array + */ + private static function byKey(): array + { + if (self::$byKey !== null) { + return self::$byKey; + } + + /** @var array $shortcodes */ + $shortcodes = json_decode((string) file_get_contents(resource_path('emoji-shortcodes.json')), true); + + $byKey = []; + + foreach ($shortcodes as $emoji) { + $byKey[self::key($emoji)] = $emoji; + } + + return self::$byKey = $byKey; + } +} diff --git a/app/NativeComponents/Conversation.php b/app/NativeComponents/Conversation.php index afe9941..519c0ea 100644 --- a/app/NativeComponents/Conversation.php +++ b/app/NativeComponents/Conversation.php @@ -13,6 +13,7 @@ use App\Devnull\Exceptions\ApiException; use App\Devnull\MuteDuration; use App\Devnull\Outbox; +use App\Devnull\ReactionEmoji; use App\Devnull\ResponseCache; use App\Devnull\SiteLink; use App\Devnull\Toasts; @@ -58,7 +59,9 @@ * Retry of its own. Dragging a message sideways slides it out of the way * with the finger, its React and Reply growing in where it was, and Pin * for an org's maintainers; let go far enough and it stays open, or it - * springs back. A double tap toggles a heart. + * springs back. A double tap toggles a heart. React shows the web's quick + * reactions in a bubble beside it, with More for the phone's own emoji + * picker. * * An org's pinned messages show as the web shows them: the latest in a bar * over the top of the thread, which opens them all in a sheet, and a pin @@ -95,18 +98,6 @@ class Conversation extends NativeComponent */ public const array QuickReactions = ['๐Ÿ˜‚', 'โœ…', 'โค๏ธ']; - /** - * The rest the sheet offers, every one of them in devnull's shortcode set. - */ - public const array MoreReactions = ['๐Ÿ‘', '๐Ÿ‘€', '๐Ÿ”ฅ', '๐ŸŽ‰', '๐Ÿš€', '๐Ÿ’ฏ', '๐Ÿ™', '๐Ÿค”', '๐Ÿ‘', '๐Ÿ˜…', '๐Ÿ˜ฎ', '๐Ÿ˜ข', '๐Ÿ™Œ', '๐Ÿ’€', 'โœจ', '๐Ÿค']; - - /** - * What the sheet shows: React's reactions for a message, or the org's pinned messages. - */ - public const string ReactionsSheet = 'reactions'; - - public const string PinnedSheet = 'pinned'; - /** * The slot beside a bubble for its actions: React and Reply, and in an org room for Pin as well, whether it shows or * not, as the web keeps it, so a pin arriving never rewraps a bubble. Then the gap between the slot and the bubble. @@ -245,23 +236,26 @@ class Conversation extends NativeComponent private array $pans = []; /** - * What the sheet shows, which stays as it was while the sheet slides away. + * Whether the sheet of the org's pinned messages is up. */ - public string $sheet = self::ReactionsSheet; + public bool $sheetOpen = false; /** - * The message React opened the reactions sheet for. + * The message whose React shows the web's quick reactions in a bubble beside it. */ - public ?int $sheetMessageId = null; - - public bool $sheetOpen = false; + public ?int $reactionsFor = null; /** - * React in the pinned sheet shows the reactions in its place, and reacting goes back to the pinned messages, as the - * web keeps its panel open behind the reactions. + * React was pressed in the pinned sheet, so the bubble shows there, and the sheet comes back after the emoji picker + * has made way for it. */ public bool $reactsFromPinned = false; + /** + * The message the phone's emoji picker is up for, if it is. + */ + public ?int $emojiPickerFor = null; + /** * The org's pinned messages, latest pinned first, which everyone in it sees whatever history they were given. * @@ -540,7 +534,8 @@ public function hideActions(): void } /** - * React: the web's reactions for the message, in a sheet. From the pinned sheet, they take its place. + * React: the web's quick reactions and More, in a bubble beside the React that was pressed, above it unless there's + * no room. The message's options stay up underneath. */ public function openReactions(int $id): void { @@ -548,11 +543,17 @@ public function openReactions(int $id): void return; } - $this->reactsFromPinned = $this->sheetOpen && $this->sheet === self::PinnedSheet; - $this->actionsForId = null; - $this->sheet = self::ReactionsSheet; - $this->sheetMessageId = $id; - $this->sheetOpen = true; + $this->reactsFromPinned = $this->sheetOpen; + $this->reactionsFor = $id; + } + + /** + * A tap outside the bubble closes it. + */ + public function reactionsClosed(): void + { + $this->reactionsFor = null; + $this->reactsFromPinned = false; } /** @@ -565,21 +566,82 @@ public function openPinned(): void } $this->actionsForId = null; + $this->reactionsFor = null; $this->reactsFromPinned = false; - $this->sheet = self::PinnedSheet; $this->sheetOpen = true; } public function messageSheetClosed(): void { $this->sheetOpen = false; + + // The pinned sheet making way for the emoji picker keeps where React came from. + if ($this->emojiPickerFor === null) { + $this->reactionsFor = null; + $this->reactsFromPinned = false; + } + } + + /** + * More reactions: the phone's own emoji picker, with its search, recents and skin tones, for any message. The + * bubble closes and the pinned sheet makes way for it. + */ + public function openEmojiPicker(int $id): void + { + if ($this->findMessage($id)?->isEvent() !== false) { + return; + } + + $this->actionsForId = null; + $this->reactionsFor = null; + $this->sheetOpen = false; + $this->emojiPickerFor = $id; + } + + /** + * Reacts with the emoji picked, as devnull takes it (see ReactionEmoji), and goes back to the pinned messages if + * React came from there. One devnull doesn't know isn't sent: a toast says so and the picker stays up for another. + */ + public function emojiPicked(string $emoji): void + { + $id = $this->emojiPickerFor; + + if ($id === null) { + return; + } + + $reaction = ReactionEmoji::forDevnull($emoji); + + if ($reaction === null) { + Toasts::failed("devnull doesn't take {$emoji} as a reaction yet. Pick another."); + + return; + } + + $this->emojiPickerFor = null; + $this->sheetOpen = $this->reactsFromPinned; $this->reactsFromPinned = false; + $this->toggleReaction($id, $reaction); + } + + /** + * Closing the picker without a pick goes back to the pinned messages if React came from there. + */ + public function emojiPickerClosed(): void + { + $this->emojiPickerFor = null; + + if ($this->reactsFromPinned) { + $this->sheetOpen = true; + $this->reactsFromPinned = false; + } } public function reply(int $id): void { $this->replyToId = $id; $this->actionsForId = null; + $this->reactionsFor = null; $this->sheetOpen = false; $this->reactsFromPinned = false; $this->composerKey++; @@ -661,7 +723,7 @@ public function unpinMessage(int $id): void $before = $this->pinned; $this->pinned = array_values(array_filter($this->pinned, fn (Message $pinned): bool => $pinned->id !== $id)); - if ($this->pinned === [] && $this->sheet === self::PinnedSheet) { + if ($this->pinned === []) { $this->sheetOpen = false; } @@ -683,17 +745,17 @@ public function heart(int $id): void /** * Leaves the emoji on the message, or takes it back, at once. If devnull says no, it goes back as it was. Picked in - * the reactions sheet, the sheet closes, or goes back to the pinned messages it was opened from. + * the bubble, the bubble closes and the message's options go away. */ public function toggleReaction(int $id, string $emoji): void { $message = $this->findMessage($id); $me = $this->me(); - if ($this->sheet === self::ReactionsSheet) { - $this->sheet = $this->reactsFromPinned ? self::PinnedSheet : self::ReactionsSheet; - $this->sheetOpen = $this->sheetOpen && $this->reactsFromPinned; + if ($this->reactionsFor !== null) { + $this->reactionsFor = null; $this->reactsFromPinned = false; + $this->actionsForId = null; } if ($message === null || $message->isPending() || $message->isEvent() || $me === null || $this->thread?->access !== Thread::Open) { @@ -924,7 +986,7 @@ private function takePinned(array $pinned, int $version): void }, $pinned); $this->pinnedVersion = $version; - if ($this->pinned === [] && $this->sheet === self::PinnedSheet) { + if ($this->pinned === []) { $this->sheetOpen = false; } } @@ -1380,9 +1442,7 @@ public function render(): View 'items' => $this->items($viewer), 'composerClearance' => self::composerClearance($this->draft, $this->replyToId !== null), 'replyTo' => $this->findMessage($this->replyToId), - 'sheetMessage' => $this->findMessage($this->sheetMessageId), 'quickReactions' => $this->quickReactions(), - 'moreReactions' => self::MoreReactions, 'canPush' => $thread !== null && $thread->access === Thread::Open, 'canPin' => $this->canPin(), 'actionsWidth' => $this->actionsWidth(), diff --git a/resources/emoji-shortcodes.json b/resources/emoji-shortcodes.json new file mode 100644 index 0000000..1699f26 --- /dev/null +++ b/resources/emoji-shortcodes.json @@ -0,0 +1,1921 @@ +{ + "+1": "๐Ÿ‘", + "-1": "๐Ÿ‘Ž", + "100": "๐Ÿ’ฏ", + "1234": "๐Ÿ”ข", + "1st_place_medal": "๐Ÿฅ‡", + "2nd_place_medal": "๐Ÿฅˆ", + "3rd_place_medal": "๐Ÿฅ‰", + "8ball": "๐ŸŽฑ", + "a": "๐Ÿ…ฐ๏ธ", + "ab": "๐Ÿ†Ž", + "abacus": "๐Ÿงฎ", + "abc": "๐Ÿ”ค", + "abcd": "๐Ÿ”ก", + "accept": "๐Ÿ‰‘", + "accordion": "๐Ÿช—", + "adhesive_bandage": "๐Ÿฉน", + "adult": "๐Ÿง‘", + "aerial_tramway": "๐Ÿšก", + "afghanistan": "๐Ÿ‡ฆ๐Ÿ‡ซ", + "airplane": "โœˆ๏ธ", + "aland_islands": "๐Ÿ‡ฆ๐Ÿ‡ฝ", + "alarm_clock": "โฐ", + "albania": "๐Ÿ‡ฆ๐Ÿ‡ฑ", + "alembic": "โš—๏ธ", + "algeria": "๐Ÿ‡ฉ๐Ÿ‡ฟ", + "alien": "๐Ÿ‘ฝ", + "ambulance": "๐Ÿš‘", + "american_samoa": "๐Ÿ‡ฆ๐Ÿ‡ธ", + "amphora": "๐Ÿบ", + "anatomical_heart": "๐Ÿซ€", + "anchor": "โš“", + "andorra": "๐Ÿ‡ฆ๐Ÿ‡ฉ", + "angel": "๐Ÿ‘ผ", + "anger": "๐Ÿ’ข", + "angola": "๐Ÿ‡ฆ๐Ÿ‡ด", + "angry": "๐Ÿ˜ ", + "anguilla": "๐Ÿ‡ฆ๐Ÿ‡ฎ", + "anguished": "๐Ÿ˜ง", + "ant": "๐Ÿœ", + "antarctica": "๐Ÿ‡ฆ๐Ÿ‡ถ", + "antigua_barbuda": "๐Ÿ‡ฆ๐Ÿ‡ฌ", + "apple": "๐ŸŽ", + "aquarius": "โ™’", + "argentina": "๐Ÿ‡ฆ๐Ÿ‡ท", + "aries": "โ™ˆ", + "armenia": "๐Ÿ‡ฆ๐Ÿ‡ฒ", + "arrow_backward": "โ—€๏ธ", + "arrow_double_down": "โฌ", + "arrow_double_up": "โซ", + "arrow_down": "โฌ‡๏ธ", + "arrow_down_small": "๐Ÿ”ฝ", + "arrow_forward": "โ–ถ๏ธ", + "arrow_heading_down": "โคต๏ธ", + "arrow_heading_up": "โคด๏ธ", + "arrow_left": "โฌ…๏ธ", + "arrow_lower_left": "โ†™๏ธ", + "arrow_lower_right": "โ†˜๏ธ", + "arrow_right": "โžก๏ธ", + "arrow_right_hook": "โ†ช๏ธ", + "arrow_up": "โฌ†๏ธ", + "arrow_up_down": "โ†•๏ธ", + "arrow_up_small": "๐Ÿ”ผ", + "arrow_upper_left": "โ†–๏ธ", + "arrow_upper_right": "โ†—๏ธ", + "arrows_clockwise": "๐Ÿ”ƒ", + "arrows_counterclockwise": "๐Ÿ”„", + "art": "๐ŸŽจ", + "articulated_lorry": "๐Ÿš›", + "artificial_satellite": "๐Ÿ›ฐ๏ธ", + "artist": "๐Ÿง‘โ€๐ŸŽจ", + "aruba": "๐Ÿ‡ฆ๐Ÿ‡ผ", + "ascension_island": "๐Ÿ‡ฆ๐Ÿ‡จ", + "asterisk": "*๏ธโƒฃ", + "astonished": "๐Ÿ˜ฒ", + "astronaut": "๐Ÿง‘โ€๐Ÿš€", + "athletic_shoe": "๐Ÿ‘Ÿ", + "atm": "๐Ÿง", + "atom_symbol": "โš›๏ธ", + "australia": "๐Ÿ‡ฆ๐Ÿ‡บ", + "austria": "๐Ÿ‡ฆ๐Ÿ‡น", + "auto_rickshaw": "๐Ÿ›บ", + "avocado": "๐Ÿฅ‘", + "axe": "๐Ÿช“", + "azerbaijan": "๐Ÿ‡ฆ๐Ÿ‡ฟ", + "b": "๐Ÿ…ฑ๏ธ", + "baby": "๐Ÿ‘ถ", + "baby_bottle": "๐Ÿผ", + "baby_chick": "๐Ÿค", + "baby_symbol": "๐Ÿšผ", + "back": "๐Ÿ”™", + "bacon": "๐Ÿฅ“", + "badger": "๐Ÿฆก", + "badminton": "๐Ÿธ", + "bagel": "๐Ÿฅฏ", + "baggage_claim": "๐Ÿ›„", + "baguette_bread": "๐Ÿฅ–", + "bahamas": "๐Ÿ‡ง๐Ÿ‡ธ", + "bahrain": "๐Ÿ‡ง๐Ÿ‡ญ", + "balance_scale": "โš–๏ธ", + "bald_man": "๐Ÿ‘จโ€๐Ÿฆฒ", + "bald_woman": "๐Ÿ‘ฉโ€๐Ÿฆฒ", + "ballet_shoes": "๐Ÿฉฐ", + "balloon": "๐ŸŽˆ", + "ballot_box": "๐Ÿ—ณ๏ธ", + "ballot_box_with_check": "โ˜‘๏ธ", + "bamboo": "๐ŸŽ", + "banana": "๐ŸŒ", + "bangbang": "โ€ผ๏ธ", + "bangladesh": "๐Ÿ‡ง๐Ÿ‡ฉ", + "banjo": "๐Ÿช•", + "bank": "๐Ÿฆ", + "bar_chart": "๐Ÿ“Š", + "barbados": "๐Ÿ‡ง๐Ÿ‡ง", + "barber": "๐Ÿ’ˆ", + "baseball": "โšพ", + "basket": "๐Ÿงบ", + "basketball": "๐Ÿ€", + "basketball_man": "โ›น๏ธโ€โ™‚๏ธ", + "basketball_woman": "โ›น๏ธโ€โ™€๏ธ", + "bat": "๐Ÿฆ‡", + "bath": "๐Ÿ›€", + "bathtub": "๐Ÿ›", + "battery": "๐Ÿ”‹", + "beach_umbrella": "๐Ÿ–๏ธ", + "beans": "๐Ÿซ˜", + "bear": "๐Ÿป", + "bearded_person": "๐Ÿง”", + "beaver": "๐Ÿฆซ", + "bed": "๐Ÿ›๏ธ", + "bee": "๐Ÿ", + "beer": "๐Ÿบ", + "beers": "๐Ÿป", + "beetle": "๐Ÿชฒ", + "beginner": "๐Ÿ”ฐ", + "belarus": "๐Ÿ‡ง๐Ÿ‡พ", + "belgium": "๐Ÿ‡ง๐Ÿ‡ช", + "belize": "๐Ÿ‡ง๐Ÿ‡ฟ", + "bell": "๐Ÿ””", + "bell_pepper": "๐Ÿซ‘", + "bellhop_bell": "๐Ÿ›Ž๏ธ", + "benin": "๐Ÿ‡ง๐Ÿ‡ฏ", + "bento": "๐Ÿฑ", + "bermuda": "๐Ÿ‡ง๐Ÿ‡ฒ", + "beverage_box": "๐Ÿงƒ", + "bhutan": "๐Ÿ‡ง๐Ÿ‡น", + "bicyclist": "๐Ÿšด", + "bike": "๐Ÿšฒ", + "biking_man": "๐Ÿšดโ€โ™‚๏ธ", + "biking_woman": "๐Ÿšดโ€โ™€๏ธ", + "bikini": "๐Ÿ‘™", + "billed_cap": "๐Ÿงข", + "biohazard": "โ˜ฃ๏ธ", + "bird": "๐Ÿฆ", + "birthday": "๐ŸŽ‚", + "bison": "๐Ÿฆฌ", + "biting_lip": "๐Ÿซฆ", + "black_bird": "๐Ÿฆโ€โฌ›", + "black_cat": "๐Ÿˆโ€โฌ›", + "black_circle": "โšซ", + "black_flag": "๐Ÿด", + "black_heart": "๐Ÿ–ค", + "black_joker": "๐Ÿƒ", + "black_large_square": "โฌ›", + "black_medium_small_square": "โ—พ", + "black_medium_square": "โ—ผ๏ธ", + "black_nib": "โœ’๏ธ", + "black_small_square": "โ–ช๏ธ", + "black_square_button": "๐Ÿ”ฒ", + "blond_haired_man": "๐Ÿ‘ฑโ€โ™‚๏ธ", + "blond_haired_person": "๐Ÿ‘ฑ", + "blond_haired_woman": "๐Ÿ‘ฑโ€โ™€๏ธ", + "blonde_woman": "๐Ÿ‘ฑโ€โ™€๏ธ", + "blossom": "๐ŸŒผ", + "blowfish": "๐Ÿก", + "blue_book": "๐Ÿ“˜", + "blue_car": "๐Ÿš™", + "blue_heart": "๐Ÿ’™", + "blue_square": "๐ŸŸฆ", + "blueberries": "๐Ÿซ", + "blush": "๐Ÿ˜Š", + "boar": "๐Ÿ—", + "boat": "โ›ต", + "bolivia": "๐Ÿ‡ง๐Ÿ‡ด", + "bomb": "๐Ÿ’ฃ", + "bone": "๐Ÿฆด", + "book": "๐Ÿ“–", + "bookmark": "๐Ÿ”–", + "bookmark_tabs": "๐Ÿ“‘", + "books": "๐Ÿ“š", + "boom": "๐Ÿ’ฅ", + "boomerang": "๐Ÿชƒ", + "boot": "๐Ÿ‘ข", + "bosnia_herzegovina": "๐Ÿ‡ง๐Ÿ‡ฆ", + "botswana": "๐Ÿ‡ง๐Ÿ‡ผ", + "bouncing_ball_man": "โ›น๏ธโ€โ™‚๏ธ", + "bouncing_ball_person": "โ›น๏ธ", + "bouncing_ball_woman": "โ›น๏ธโ€โ™€๏ธ", + "bouquet": "๐Ÿ’", + "bouvet_island": "๐Ÿ‡ง๐Ÿ‡ป", + "bow": "๐Ÿ™‡", + "bow_and_arrow": "๐Ÿน", + "bowing_man": "๐Ÿ™‡โ€โ™‚๏ธ", + "bowing_woman": "๐Ÿ™‡โ€โ™€๏ธ", + "bowl_with_spoon": "๐Ÿฅฃ", + "bowling": "๐ŸŽณ", + "boxing_glove": "๐ŸฅŠ", + "boy": "๐Ÿ‘ฆ", + "brain": "๐Ÿง ", + "brazil": "๐Ÿ‡ง๐Ÿ‡ท", + "bread": "๐Ÿž", + "breast_feeding": "๐Ÿคฑ", + "bricks": "๐Ÿงฑ", + "bride_with_veil": "๐Ÿ‘ฐโ€โ™€๏ธ", + "bridge_at_night": "๐ŸŒ‰", + "briefcase": "๐Ÿ’ผ", + "british_indian_ocean_territory": "๐Ÿ‡ฎ๐Ÿ‡ด", + "british_virgin_islands": "๐Ÿ‡ป๐Ÿ‡ฌ", + "broccoli": "๐Ÿฅฆ", + "broken_heart": "๐Ÿ’”", + "broom": "๐Ÿงน", + "brown_circle": "๐ŸŸค", + "brown_heart": "๐ŸคŽ", + "brown_square": "๐ŸŸซ", + "brunei": "๐Ÿ‡ง๐Ÿ‡ณ", + "bubble_tea": "๐Ÿง‹", + "bubbles": "๐Ÿซง", + "bucket": "๐Ÿชฃ", + "bug": "๐Ÿ›", + "building_construction": "๐Ÿ—๏ธ", + "bulb": "๐Ÿ’ก", + "bulgaria": "๐Ÿ‡ง๐Ÿ‡ฌ", + "bullettrain_front": "๐Ÿš…", + "bullettrain_side": "๐Ÿš„", + "burkina_faso": "๐Ÿ‡ง๐Ÿ‡ซ", + "burrito": "๐ŸŒฏ", + "burundi": "๐Ÿ‡ง๐Ÿ‡ฎ", + "bus": "๐ŸšŒ", + "business_suit_levitating": "๐Ÿ•ด๏ธ", + "busstop": "๐Ÿš", + "bust_in_silhouette": "๐Ÿ‘ค", + "busts_in_silhouette": "๐Ÿ‘ฅ", + "butter": "๐Ÿงˆ", + "butterfly": "๐Ÿฆ‹", + "cactus": "๐ŸŒต", + "cake": "๐Ÿฐ", + "calendar": "๐Ÿ“†", + "call_me_hand": "๐Ÿค™", + "calling": "๐Ÿ“ฒ", + "cambodia": "๐Ÿ‡ฐ๐Ÿ‡ญ", + "camel": "๐Ÿซ", + "camera": "๐Ÿ“ท", + "camera_flash": "๐Ÿ“ธ", + "cameroon": "๐Ÿ‡จ๐Ÿ‡ฒ", + "camping": "๐Ÿ•๏ธ", + "canada": "๐Ÿ‡จ๐Ÿ‡ฆ", + "canary_islands": "๐Ÿ‡ฎ๐Ÿ‡จ", + "cancer": "โ™‹", + "candle": "๐Ÿ•ฏ๏ธ", + "candy": "๐Ÿฌ", + "canned_food": "๐Ÿฅซ", + "canoe": "๐Ÿ›ถ", + "cape_verde": "๐Ÿ‡จ๐Ÿ‡ป", + "capital_abcd": "๐Ÿ” ", + "capricorn": "โ™‘", + "car": "๐Ÿš—", + "card_file_box": "๐Ÿ—ƒ๏ธ", + "card_index": "๐Ÿ“‡", + "card_index_dividers": "๐Ÿ—‚๏ธ", + "caribbean_netherlands": "๐Ÿ‡ง๐Ÿ‡ถ", + "carousel_horse": "๐ŸŽ ", + "carpentry_saw": "๐Ÿชš", + "carrot": "๐Ÿฅ•", + "cartwheeling": "๐Ÿคธ", + "cat": "๐Ÿฑ", + "cat2": "๐Ÿˆ", + "cayman_islands": "๐Ÿ‡ฐ๐Ÿ‡พ", + "cd": "๐Ÿ’ฟ", + "central_african_republic": "๐Ÿ‡จ๐Ÿ‡ซ", + "ceuta_melilla": "๐Ÿ‡ช๐Ÿ‡ฆ", + "chad": "๐Ÿ‡น๐Ÿ‡ฉ", + "chains": "โ›“๏ธ", + "chair": "๐Ÿช‘", + "champagne": "๐Ÿพ", + "chart": "๐Ÿ’น", + "chart_with_downwards_trend": "๐Ÿ“‰", + "chart_with_upwards_trend": "๐Ÿ“ˆ", + "checkered_flag": "๐Ÿ", + "cheese": "๐Ÿง€", + "cherries": "๐Ÿ’", + "cherry_blossom": "๐ŸŒธ", + "chess_pawn": "โ™Ÿ๏ธ", + "chestnut": "๐ŸŒฐ", + "chicken": "๐Ÿ”", + "child": "๐Ÿง’", + "children_crossing": "๐Ÿšธ", + "chile": "๐Ÿ‡จ๐Ÿ‡ฑ", + "chipmunk": "๐Ÿฟ๏ธ", + "chocolate_bar": "๐Ÿซ", + "chopsticks": "๐Ÿฅข", + "christmas_island": "๐Ÿ‡จ๐Ÿ‡ฝ", + "christmas_tree": "๐ŸŽ„", + "church": "โ›ช", + "cinema": "๐ŸŽฆ", + "circus_tent": "๐ŸŽช", + "city_sunrise": "๐ŸŒ‡", + "city_sunset": "๐ŸŒ†", + "cityscape": "๐Ÿ™๏ธ", + "cl": "๐Ÿ†‘", + "clamp": "๐Ÿ—œ๏ธ", + "clap": "๐Ÿ‘", + "clapper": "๐ŸŽฌ", + "classical_building": "๐Ÿ›๏ธ", + "climbing": "๐Ÿง—", + "climbing_man": "๐Ÿง—โ€โ™‚๏ธ", + "climbing_woman": "๐Ÿง—โ€โ™€๏ธ", + "clinking_glasses": "๐Ÿฅ‚", + "clipboard": "๐Ÿ“‹", + "clipperton_island": "๐Ÿ‡จ๐Ÿ‡ต", + "clock1": "๐Ÿ•", + "clock10": "๐Ÿ•™", + "clock1030": "๐Ÿ•ฅ", + "clock11": "๐Ÿ•š", + "clock1130": "๐Ÿ•ฆ", + "clock12": "๐Ÿ•›", + "clock1230": "๐Ÿ•ง", + "clock130": "๐Ÿ•œ", + "clock2": "๐Ÿ•‘", + "clock230": "๐Ÿ•", + "clock3": "๐Ÿ•’", + "clock330": "๐Ÿ•ž", + "clock4": "๐Ÿ•“", + "clock430": "๐Ÿ•Ÿ", + "clock5": "๐Ÿ•”", + "clock530": "๐Ÿ• ", + "clock6": "๐Ÿ••", + "clock630": "๐Ÿ•ก", + "clock7": "๐Ÿ•–", + "clock730": "๐Ÿ•ข", + "clock8": "๐Ÿ•—", + "clock830": "๐Ÿ•ฃ", + "clock9": "๐Ÿ•˜", + "clock930": "๐Ÿ•ค", + "closed_book": "๐Ÿ“•", + "closed_lock_with_key": "๐Ÿ”", + "closed_umbrella": "๐ŸŒ‚", + "cloud": "โ˜๏ธ", + "cloud_with_lightning": "๐ŸŒฉ๏ธ", + "cloud_with_lightning_and_rain": "โ›ˆ๏ธ", + "cloud_with_rain": "๐ŸŒง๏ธ", + "cloud_with_snow": "๐ŸŒจ๏ธ", + "clown_face": "๐Ÿคก", + "clubs": "โ™ฃ๏ธ", + "cn": "๐Ÿ‡จ๐Ÿ‡ณ", + "coat": "๐Ÿงฅ", + "cockroach": "๐Ÿชณ", + "cocktail": "๐Ÿธ", + "coconut": "๐Ÿฅฅ", + "cocos_islands": "๐Ÿ‡จ๐Ÿ‡จ", + "coffee": "โ˜•", + "coffin": "โšฐ๏ธ", + "coin": "๐Ÿช™", + "cold_face": "๐Ÿฅถ", + "cold_sweat": "๐Ÿ˜ฐ", + "collision": "๐Ÿ’ฅ", + "colombia": "๐Ÿ‡จ๐Ÿ‡ด", + "comet": "โ˜„๏ธ", + "comoros": "๐Ÿ‡ฐ๐Ÿ‡ฒ", + "compass": "๐Ÿงญ", + "computer": "๐Ÿ’ป", + "computer_mouse": "๐Ÿ–ฑ๏ธ", + "confetti_ball": "๐ŸŽŠ", + "confounded": "๐Ÿ˜–", + "confused": "๐Ÿ˜•", + "congo_brazzaville": "๐Ÿ‡จ๐Ÿ‡ฌ", + "congo_kinshasa": "๐Ÿ‡จ๐Ÿ‡ฉ", + "congratulations": "ใŠ—๏ธ", + "construction": "๐Ÿšง", + "construction_worker": "๐Ÿ‘ท", + "construction_worker_man": "๐Ÿ‘ทโ€โ™‚๏ธ", + "construction_worker_woman": "๐Ÿ‘ทโ€โ™€๏ธ", + "control_knobs": "๐ŸŽ›๏ธ", + "convenience_store": "๐Ÿช", + "cook": "๐Ÿง‘โ€๐Ÿณ", + "cook_islands": "๐Ÿ‡จ๐Ÿ‡ฐ", + "cookie": "๐Ÿช", + "cool": "๐Ÿ†’", + "cop": "๐Ÿ‘ฎ", + "copyright": "ยฉ๏ธ", + "coral": "๐Ÿชธ", + "corn": "๐ŸŒฝ", + "costa_rica": "๐Ÿ‡จ๐Ÿ‡ท", + "cote_divoire": "๐Ÿ‡จ๐Ÿ‡ฎ", + "couch_and_lamp": "๐Ÿ›‹๏ธ", + "couple": "๐Ÿ‘ซ", + "couple_with_heart": "๐Ÿ’‘", + "couple_with_heart_man_man": "๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ", + "couple_with_heart_woman_man": "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘จ", + "couple_with_heart_woman_woman": "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ‘ฉ", + "couplekiss": "๐Ÿ’", + "couplekiss_man_man": "๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ", + "couplekiss_man_woman": "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ", + "couplekiss_woman_woman": "๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ", + "cow": "๐Ÿฎ", + "cow2": "๐Ÿ„", + "cowboy_hat_face": "๐Ÿค ", + "crab": "๐Ÿฆ€", + "crayon": "๐Ÿ–๏ธ", + "credit_card": "๐Ÿ’ณ", + "crescent_moon": "๐ŸŒ™", + "cricket": "๐Ÿฆ—", + "cricket_game": "๐Ÿ", + "croatia": "๐Ÿ‡ญ๐Ÿ‡ท", + "crocodile": "๐ŸŠ", + "croissant": "๐Ÿฅ", + "crossed_fingers": "๐Ÿคž", + "crossed_flags": "๐ŸŽŒ", + "crossed_swords": "โš”๏ธ", + "crown": "๐Ÿ‘‘", + "crutch": "๐Ÿฉผ", + "cry": "๐Ÿ˜ข", + "crying_cat_face": "๐Ÿ˜ฟ", + "crystal_ball": "๐Ÿ”ฎ", + "cuba": "๐Ÿ‡จ๐Ÿ‡บ", + "cucumber": "๐Ÿฅ’", + "cup_with_straw": "๐Ÿฅค", + "cupcake": "๐Ÿง", + "cupid": "๐Ÿ’˜", + "curacao": "๐Ÿ‡จ๐Ÿ‡ผ", + "curling_stone": "๐ŸฅŒ", + "curly_haired_man": "๐Ÿ‘จโ€๐Ÿฆฑ", + "curly_haired_woman": "๐Ÿ‘ฉโ€๐Ÿฆฑ", + "curly_loop": "โžฐ", + "currency_exchange": "๐Ÿ’ฑ", + "curry": "๐Ÿ›", + "cursing_face": "๐Ÿคฌ", + "custard": "๐Ÿฎ", + "customs": "๐Ÿ›ƒ", + "cut_of_meat": "๐Ÿฅฉ", + "cyclone": "๐ŸŒ€", + "cyprus": "๐Ÿ‡จ๐Ÿ‡พ", + "czech_republic": "๐Ÿ‡จ๐Ÿ‡ฟ", + "dagger": "๐Ÿ—ก๏ธ", + "dancer": "๐Ÿ’ƒ", + "dancers": "๐Ÿ‘ฏ", + "dancing_men": "๐Ÿ‘ฏโ€โ™‚๏ธ", + "dancing_women": "๐Ÿ‘ฏโ€โ™€๏ธ", + "dango": "๐Ÿก", + "dark_sunglasses": "๐Ÿ•ถ๏ธ", + "dart": "๐ŸŽฏ", + "dash": "๐Ÿ’จ", + "date": "๐Ÿ“…", + "de": "๐Ÿ‡ฉ๐Ÿ‡ช", + "deaf_man": "๐Ÿงโ€โ™‚๏ธ", + "deaf_person": "๐Ÿง", + "deaf_woman": "๐Ÿงโ€โ™€๏ธ", + "deciduous_tree": "๐ŸŒณ", + "deer": "๐ŸฆŒ", + "denmark": "๐Ÿ‡ฉ๐Ÿ‡ฐ", + "department_store": "๐Ÿฌ", + "derelict_house": "๐Ÿš๏ธ", + "desert": "๐Ÿœ๏ธ", + "desert_island": "๐Ÿ๏ธ", + "desktop_computer": "๐Ÿ–ฅ๏ธ", + "detective": "๐Ÿ•ต๏ธ", + "diamond_shape_with_a_dot_inside": "๐Ÿ’ ", + "diamonds": "โ™ฆ๏ธ", + "diego_garcia": "๐Ÿ‡ฉ๐Ÿ‡ฌ", + "disappointed": "๐Ÿ˜ž", + "disappointed_relieved": "๐Ÿ˜ฅ", + "disguised_face": "๐Ÿฅธ", + "dislike": "๐Ÿ‘Ž", + "diving_mask": "๐Ÿคฟ", + "diya_lamp": "๐Ÿช”", + "dizzy": "๐Ÿ’ซ", + "dizzy_face": "๐Ÿ˜ต", + "djibouti": "๐Ÿ‡ฉ๐Ÿ‡ฏ", + "dna": "๐Ÿงฌ", + "do_not_litter": "๐Ÿšฏ", + "dodo": "๐Ÿฆค", + "dog": "๐Ÿถ", + "dog2": "๐Ÿ•", + "dollar": "๐Ÿ’ต", + "dolls": "๐ŸŽŽ", + "dolphin": "๐Ÿฌ", + "dominica": "๐Ÿ‡ฉ๐Ÿ‡ฒ", + "dominican_republic": "๐Ÿ‡ฉ๐Ÿ‡ด", + "donkey": "๐Ÿซ", + "door": "๐Ÿšช", + "dotted_line_face": "๐Ÿซฅ", + "doughnut": "๐Ÿฉ", + "dove": "๐Ÿ•Š๏ธ", + "dragon": "๐Ÿ‰", + "dragon_face": "๐Ÿฒ", + "dress": "๐Ÿ‘—", + "dromedary_camel": "๐Ÿช", + "drooling_face": "๐Ÿคค", + "drop_of_blood": "๐Ÿฉธ", + "droplet": "๐Ÿ’ง", + "drum": "๐Ÿฅ", + "duck": "๐Ÿฆ†", + "dumpling": "๐ŸฅŸ", + "dvd": "๐Ÿ“€", + "e-mail": "๐Ÿ“ง", + "eagle": "๐Ÿฆ…", + "ear": "๐Ÿ‘‚", + "ear_of_rice": "๐ŸŒพ", + "ear_with_hearing_aid": "๐Ÿฆป", + "earth_africa": "๐ŸŒ", + "earth_americas": "๐ŸŒŽ", + "earth_asia": "๐ŸŒ", + "ecuador": "๐Ÿ‡ช๐Ÿ‡จ", + "egg": "๐Ÿฅš", + "eggplant": "๐Ÿ†", + "egypt": "๐Ÿ‡ช๐Ÿ‡ฌ", + "eight": "8๏ธโƒฃ", + "eight_pointed_black_star": "โœด๏ธ", + "eight_spoked_asterisk": "โœณ๏ธ", + "eject_button": "โ๏ธ", + "el_salvador": "๐Ÿ‡ธ๐Ÿ‡ป", + "electric_plug": "๐Ÿ”Œ", + "elephant": "๐Ÿ˜", + "elevator": "๐Ÿ›—", + "elf": "๐Ÿง", + "elf_man": "๐Ÿงโ€โ™‚๏ธ", + "elf_woman": "๐Ÿงโ€โ™€๏ธ", + "email": "๐Ÿ“ง", + "empty_nest": "๐Ÿชน", + "end": "๐Ÿ”š", + "england": "๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ", + "envelope": "โœ‰๏ธ", + "envelope_with_arrow": "๐Ÿ“ฉ", + "equatorial_guinea": "๐Ÿ‡ฌ๐Ÿ‡ถ", + "eritrea": "๐Ÿ‡ช๐Ÿ‡ท", + "es": "๐Ÿ‡ช๐Ÿ‡ธ", + "estonia": "๐Ÿ‡ช๐Ÿ‡ช", + "ethiopia": "๐Ÿ‡ช๐Ÿ‡น", + "eu": "๐Ÿ‡ช๐Ÿ‡บ", + "euro": "๐Ÿ’ถ", + "european_castle": "๐Ÿฐ", + "european_post_office": "๐Ÿค", + "european_union": "๐Ÿ‡ช๐Ÿ‡บ", + "evergreen_tree": "๐ŸŒฒ", + "exclamation": "โ—", + "exploding_head": "๐Ÿคฏ", + "expressionless": "๐Ÿ˜‘", + "eye": "๐Ÿ‘๏ธ", + "eye_speech_bubble": "๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ", + "eyeglasses": "๐Ÿ‘“", + "eyes": "๐Ÿ‘€", + "face_exhaling": "๐Ÿ˜ฎโ€๐Ÿ’จ", + "face_holding_back_tears": "๐Ÿฅน", + "face_in_clouds": "๐Ÿ˜ถโ€๐ŸŒซ๏ธ", + "face_with_diagonal_mouth": "๐Ÿซค", + "face_with_head_bandage": "๐Ÿค•", + "face_with_open_eyes_and_hand_over_mouth": "๐Ÿซข", + "face_with_peeking_eye": "๐Ÿซฃ", + "face_with_spiral_eyes": "๐Ÿ˜ตโ€๐Ÿ’ซ", + "face_with_thermometer": "๐Ÿค’", + "facepalm": "๐Ÿคฆ", + "facepunch": "๐Ÿ‘Š", + "factory": "๐Ÿญ", + "factory_worker": "๐Ÿง‘โ€๐Ÿญ", + "fairy": "๐Ÿงš", + "fairy_man": "๐Ÿงšโ€โ™‚๏ธ", + "fairy_woman": "๐Ÿงšโ€โ™€๏ธ", + "falafel": "๐Ÿง†", + "falkland_islands": "๐Ÿ‡ซ๐Ÿ‡ฐ", + "fallen_leaf": "๐Ÿ‚", + "family": "๐Ÿ‘ช", + "family_man_boy": "๐Ÿ‘จโ€๐Ÿ‘ฆ", + "family_man_boy_boy": "๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ", + "family_man_girl": "๐Ÿ‘จโ€๐Ÿ‘ง", + "family_man_girl_boy": "๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", + "family_man_girl_girl": "๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง", + "family_man_man_boy": "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆ", + "family_man_man_boy_boy": "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ", + "family_man_man_girl": "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘ง", + "family_man_man_girl_boy": "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", + "family_man_man_girl_girl": "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ง", + "family_man_woman_boy": "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ", + "family_man_woman_boy_boy": "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ", + "family_man_woman_girl": "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง", + "family_man_woman_girl_boy": "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", + "family_man_woman_girl_girl": "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง", + "family_woman_boy": "๐Ÿ‘ฉโ€๐Ÿ‘ฆ", + "family_woman_boy_boy": "๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ", + "family_woman_girl": "๐Ÿ‘ฉโ€๐Ÿ‘ง", + "family_woman_girl_boy": "๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", + "family_woman_girl_girl": "๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง", + "family_woman_woman_boy": "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ", + "family_woman_woman_boy_boy": "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ", + "family_woman_woman_girl": "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ง", + "family_woman_woman_girl_boy": "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", + "family_woman_woman_girl_girl": "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง", + "farmer": "๐Ÿง‘โ€๐ŸŒพ", + "faroe_islands": "๐Ÿ‡ซ๐Ÿ‡ด", + "fast_forward": "โฉ", + "fax": "๐Ÿ“ ", + "fearful": "๐Ÿ˜จ", + "feather": "๐Ÿชถ", + "feet": "๐Ÿพ", + "female_detective": "๐Ÿ•ต๏ธโ€โ™€๏ธ", + "female_sign": "โ™€๏ธ", + "ferris_wheel": "๐ŸŽก", + "ferry": "โ›ด๏ธ", + "field_hockey": "๐Ÿ‘", + "fiji": "๐Ÿ‡ซ๐Ÿ‡ฏ", + "file_cabinet": "๐Ÿ—„๏ธ", + "file_folder": "๐Ÿ“", + "film_projector": "๐Ÿ“ฝ๏ธ", + "film_strip": "๐ŸŽž๏ธ", + "finland": "๐Ÿ‡ซ๐Ÿ‡ฎ", + "fire": "๐Ÿ”ฅ", + "fire_engine": "๐Ÿš’", + "fire_extinguisher": "๐Ÿงฏ", + "firecracker": "๐Ÿงจ", + "firefighter": "๐Ÿง‘โ€๐Ÿš’", + "fireworks": "๐ŸŽ†", + "first_quarter_moon": "๐ŸŒ“", + "first_quarter_moon_with_face": "๐ŸŒ›", + "fish": "๐ŸŸ", + "fish_cake": "๐Ÿฅ", + "fishing_pole_and_fish": "๐ŸŽฃ", + "fist": "โœŠ", + "fist_left": "๐Ÿค›", + "fist_oncoming": "๐Ÿ‘Š", + "fist_raised": "โœŠ", + "fist_right": "๐Ÿคœ", + "five": "5๏ธโƒฃ", + "flags": "๐ŸŽ", + "flamingo": "๐Ÿฆฉ", + "flashlight": "๐Ÿ”ฆ", + "flat_shoe": "๐Ÿฅฟ", + "flatbread": "๐Ÿซ“", + "fleur_de_lis": "โšœ๏ธ", + "flight_arrival": "๐Ÿ›ฌ", + "flight_departure": "๐Ÿ›ซ", + "flipper": "๐Ÿฌ", + "floppy_disk": "๐Ÿ’พ", + "flower_playing_cards": "๐ŸŽด", + "flushed": "๐Ÿ˜ณ", + "flute": "๐Ÿชˆ", + "fly": "๐Ÿชฐ", + "flying_disc": "๐Ÿฅ", + "flying_saucer": "๐Ÿ›ธ", + "fog": "๐ŸŒซ๏ธ", + "foggy": "๐ŸŒ", + "folding_hand_fan": "๐Ÿชญ", + "fondue": "๐Ÿซ•", + "foot": "๐Ÿฆถ", + "football": "๐Ÿˆ", + "footprints": "๐Ÿ‘ฃ", + "fork_and_knife": "๐Ÿด", + "fortune_cookie": "๐Ÿฅ ", + "fountain": "โ›ฒ", + "fountain_pen": "๐Ÿ–‹๏ธ", + "four": "4๏ธโƒฃ", + "four_leaf_clover": "๐Ÿ€", + "fox_face": "๐ŸฆŠ", + "fr": "๐Ÿ‡ซ๐Ÿ‡ท", + "framed_picture": "๐Ÿ–ผ๏ธ", + "free": "๐Ÿ†“", + "french_guiana": "๐Ÿ‡ฌ๐Ÿ‡ซ", + "french_polynesia": "๐Ÿ‡ต๐Ÿ‡ซ", + "french_southern_territories": "๐Ÿ‡น๐Ÿ‡ซ", + "fried_egg": "๐Ÿณ", + "fried_shrimp": "๐Ÿค", + "fries": "๐ŸŸ", + "frog": "๐Ÿธ", + "frowning": "๐Ÿ˜ฆ", + "frowning_face": "โ˜น๏ธ", + "frowning_man": "๐Ÿ™โ€โ™‚๏ธ", + "frowning_person": "๐Ÿ™", + "frowning_woman": "๐Ÿ™โ€โ™€๏ธ", + "fu": "๐Ÿ–•", + "fuelpump": "โ›ฝ", + "full_moon": "๐ŸŒ•", + "full_moon_with_face": "๐ŸŒ", + "funeral_urn": "โšฑ๏ธ", + "gabon": "๐Ÿ‡ฌ๐Ÿ‡ฆ", + "gambia": "๐Ÿ‡ฌ๐Ÿ‡ฒ", + "game_die": "๐ŸŽฒ", + "garlic": "๐Ÿง„", + "gb": "๐Ÿ‡ฌ๐Ÿ‡ง", + "gear": "โš™๏ธ", + "gem": "๐Ÿ’Ž", + "gemini": "โ™Š", + "genie": "๐Ÿงž", + "genie_man": "๐Ÿงžโ€โ™‚๏ธ", + "genie_woman": "๐Ÿงžโ€โ™€๏ธ", + "georgia": "๐Ÿ‡ฌ๐Ÿ‡ช", + "ghana": "๐Ÿ‡ฌ๐Ÿ‡ญ", + "ghost": "๐Ÿ‘ป", + "gibraltar": "๐Ÿ‡ฌ๐Ÿ‡ฎ", + "gift": "๐ŸŽ", + "gift_heart": "๐Ÿ’", + "ginger_root": "๐Ÿซš", + "giraffe": "๐Ÿฆ’", + "girl": "๐Ÿ‘ง", + "globe_with_meridians": "๐ŸŒ", + "gloves": "๐Ÿงค", + "goal_net": "๐Ÿฅ…", + "goat": "๐Ÿ", + "goggles": "๐Ÿฅฝ", + "golf": "โ›ณ", + "golfing": "๐ŸŒ๏ธ", + "golfing_man": "๐ŸŒ๏ธโ€โ™‚๏ธ", + "golfing_woman": "๐ŸŒ๏ธโ€โ™€๏ธ", + "goose": "๐Ÿชฟ", + "gorilla": "๐Ÿฆ", + "grapes": "๐Ÿ‡", + "greece": "๐Ÿ‡ฌ๐Ÿ‡ท", + "green_apple": "๐Ÿ", + "green_book": "๐Ÿ“—", + "green_circle": "๐ŸŸข", + "green_heart": "๐Ÿ’š", + "green_salad": "๐Ÿฅ—", + "green_square": "๐ŸŸฉ", + "greenland": "๐Ÿ‡ฌ๐Ÿ‡ฑ", + "grenada": "๐Ÿ‡ฌ๐Ÿ‡ฉ", + "grey_exclamation": "โ•", + "grey_heart": "๐Ÿฉถ", + "grey_question": "โ”", + "grimacing": "๐Ÿ˜ฌ", + "grin": "๐Ÿ˜", + "grinning": "๐Ÿ˜€", + "guadeloupe": "๐Ÿ‡ฌ๐Ÿ‡ต", + "guam": "๐Ÿ‡ฌ๐Ÿ‡บ", + "guard": "๐Ÿ’‚", + "guardsman": "๐Ÿ’‚โ€โ™‚๏ธ", + "guardswoman": "๐Ÿ’‚โ€โ™€๏ธ", + "guatemala": "๐Ÿ‡ฌ๐Ÿ‡น", + "guernsey": "๐Ÿ‡ฌ๐Ÿ‡ฌ", + "guide_dog": "๐Ÿฆฎ", + "guinea": "๐Ÿ‡ฌ๐Ÿ‡ณ", + "guinea_bissau": "๐Ÿ‡ฌ๐Ÿ‡ผ", + "guitar": "๐ŸŽธ", + "gun": "๐Ÿ”ซ", + "guyana": "๐Ÿ‡ฌ๐Ÿ‡พ", + "hair_pick": "๐Ÿชฎ", + "haircut": "๐Ÿ’‡", + "haircut_man": "๐Ÿ’‡โ€โ™‚๏ธ", + "haircut_woman": "๐Ÿ’‡โ€โ™€๏ธ", + "haiti": "๐Ÿ‡ญ๐Ÿ‡น", + "hamburger": "๐Ÿ”", + "hammer": "๐Ÿ”จ", + "hammer_and_pick": "โš’๏ธ", + "hammer_and_wrench": "๐Ÿ› ๏ธ", + "hamsa": "๐Ÿชฌ", + "hamster": "๐Ÿน", + "hand": "โœ‹", + "hand_over_mouth": "๐Ÿคญ", + "hand_with_index_finger_and_thumb_crossed": "๐Ÿซฐ", + "handbag": "๐Ÿ‘œ", + "handball_person": "๐Ÿคพ", + "handshake": "๐Ÿค", + "hankey": "๐Ÿ’ฉ", + "hash": "#๏ธโƒฃ", + "hatched_chick": "๐Ÿฅ", + "hatching_chick": "๐Ÿฃ", + "headphones": "๐ŸŽง", + "headstone": "๐Ÿชฆ", + "health_worker": "๐Ÿง‘โ€โš•๏ธ", + "hear_no_evil": "๐Ÿ™‰", + "heard_mcdonald_islands": "๐Ÿ‡ญ๐Ÿ‡ฒ", + "heart": "โค๏ธ", + "heart_decoration": "๐Ÿ’Ÿ", + "heart_eyes": "๐Ÿ˜", + "heart_eyes_cat": "๐Ÿ˜ป", + "heart_hands": "๐Ÿซถ", + "heart_on_fire": "โค๏ธโ€๐Ÿ”ฅ", + "heartbeat": "๐Ÿ’“", + "heartpulse": "๐Ÿ’—", + "hearts": "โ™ฅ๏ธ", + "heavy_check_mark": "โœ”๏ธ", + "heavy_division_sign": "โž—", + "heavy_dollar_sign": "๐Ÿ’ฒ", + "heavy_equals_sign": "๐ŸŸฐ", + "heavy_exclamation_mark": "โ—", + "heavy_heart_exclamation": "โฃ๏ธ", + "heavy_minus_sign": "โž–", + "heavy_multiplication_x": "โœ–๏ธ", + "heavy_plus_sign": "โž•", + "hedgehog": "๐Ÿฆ”", + "helicopter": "๐Ÿš", + "herb": "๐ŸŒฟ", + "hibiscus": "๐ŸŒบ", + "high_brightness": "๐Ÿ”†", + "high_heel": "๐Ÿ‘ ", + "hiking_boot": "๐Ÿฅพ", + "hindu_temple": "๐Ÿ›•", + "hippopotamus": "๐Ÿฆ›", + "hocho": "๐Ÿ”ช", + "hole": "๐Ÿ•ณ๏ธ", + "honduras": "๐Ÿ‡ญ๐Ÿ‡ณ", + "honey_pot": "๐Ÿฏ", + "honeybee": "๐Ÿ", + "hong_kong": "๐Ÿ‡ญ๐Ÿ‡ฐ", + "hook": "๐Ÿช", + "horse": "๐Ÿด", + "horse_racing": "๐Ÿ‡", + "hospital": "๐Ÿฅ", + "hot_face": "๐Ÿฅต", + "hot_pepper": "๐ŸŒถ๏ธ", + "hotdog": "๐ŸŒญ", + "hotel": "๐Ÿจ", + "hotsprings": "โ™จ๏ธ", + "hourglass": "โŒ›", + "hourglass_flowing_sand": "โณ", + "house": "๐Ÿ ", + "house_with_garden": "๐Ÿก", + "houses": "๐Ÿ˜๏ธ", + "hugs": "๐Ÿค—", + "hungary": "๐Ÿ‡ญ๐Ÿ‡บ", + "hushed": "๐Ÿ˜ฏ", + "hut": "๐Ÿ›–", + "hyacinth": "๐Ÿชป", + "ice_cream": "๐Ÿจ", + "ice_cube": "๐ŸงŠ", + "ice_hockey": "๐Ÿ’", + "ice_skate": "โ›ธ๏ธ", + "icecream": "๐Ÿฆ", + "iceland": "๐Ÿ‡ฎ๐Ÿ‡ธ", + "id": "๐Ÿ†”", + "identification_card": "๐Ÿชช", + "ideograph_advantage": "๐Ÿ‰", + "imp": "๐Ÿ‘ฟ", + "inbox_tray": "๐Ÿ“ฅ", + "incoming_envelope": "๐Ÿ“จ", + "index_pointing_at_the_viewer": "๐Ÿซต", + "india": "๐Ÿ‡ฎ๐Ÿ‡ณ", + "indonesia": "๐Ÿ‡ฎ๐Ÿ‡ฉ", + "infinity": "โ™พ๏ธ", + "information_desk_person": "๐Ÿ’", + "information_source": "โ„น๏ธ", + "innocent": "๐Ÿ˜‡", + "interrobang": "โ‰๏ธ", + "iphone": "๐Ÿ“ฑ", + "iran": "๐Ÿ‡ฎ๐Ÿ‡ท", + "iraq": "๐Ÿ‡ฎ๐Ÿ‡ถ", + "ireland": "๐Ÿ‡ฎ๐Ÿ‡ช", + "isle_of_man": "๐Ÿ‡ฎ๐Ÿ‡ฒ", + "israel": "๐Ÿ‡ฎ๐Ÿ‡ฑ", + "it": "๐Ÿ‡ฎ๐Ÿ‡น", + "izakaya_lantern": "๐Ÿฎ", + "jack_o_lantern": "๐ŸŽƒ", + "jamaica": "๐Ÿ‡ฏ๐Ÿ‡ฒ", + "japan": "๐Ÿ—พ", + "japanese_castle": "๐Ÿฏ", + "japanese_goblin": "๐Ÿ‘บ", + "japanese_ogre": "๐Ÿ‘น", + "jar": "๐Ÿซ™", + "jeans": "๐Ÿ‘–", + "jellyfish": "๐Ÿชผ", + "jersey": "๐Ÿ‡ฏ๐Ÿ‡ช", + "jigsaw": "๐Ÿงฉ", + "jordan": "๐Ÿ‡ฏ๐Ÿ‡ด", + "joy": "๐Ÿ˜‚", + "joy_cat": "๐Ÿ˜น", + "joystick": "๐Ÿ•น๏ธ", + "jp": "๐Ÿ‡ฏ๐Ÿ‡ต", + "judge": "๐Ÿง‘โ€โš–๏ธ", + "juggling_person": "๐Ÿคน", + "kaaba": "๐Ÿ•‹", + "kangaroo": "๐Ÿฆ˜", + "kazakhstan": "๐Ÿ‡ฐ๐Ÿ‡ฟ", + "kenya": "๐Ÿ‡ฐ๐Ÿ‡ช", + "key": "๐Ÿ”‘", + "keyboard": "โŒจ๏ธ", + "keycap_ten": "๐Ÿ”Ÿ", + "khanda": "๐Ÿชฏ", + "kick_scooter": "๐Ÿ›ด", + "kimono": "๐Ÿ‘˜", + "kiribati": "๐Ÿ‡ฐ๐Ÿ‡ฎ", + "kiss": "๐Ÿ’‹", + "kissing": "๐Ÿ˜—", + "kissing_cat": "๐Ÿ˜ฝ", + "kissing_closed_eyes": "๐Ÿ˜š", + "kissing_heart": "๐Ÿ˜˜", + "kissing_smiling_eyes": "๐Ÿ˜™", + "kite": "๐Ÿช", + "kiwi_fruit": "๐Ÿฅ", + "kneeling_man": "๐ŸงŽโ€โ™‚๏ธ", + "kneeling_person": "๐ŸงŽ", + "kneeling_woman": "๐ŸงŽโ€โ™€๏ธ", + "knife": "๐Ÿ”ช", + "knot": "๐Ÿชข", + "koala": "๐Ÿจ", + "koko": "๐Ÿˆ", + "kosovo": "๐Ÿ‡ฝ๐Ÿ‡ฐ", + "kr": "๐Ÿ‡ฐ๐Ÿ‡ท", + "kuwait": "๐Ÿ‡ฐ๐Ÿ‡ผ", + "kyrgyzstan": "๐Ÿ‡ฐ๐Ÿ‡ฌ", + "lab_coat": "๐Ÿฅผ", + "label": "๐Ÿท๏ธ", + "lacrosse": "๐Ÿฅ", + "ladder": "๐Ÿชœ", + "lady_beetle": "๐Ÿž", + "lantern": "๐Ÿฎ", + "laos": "๐Ÿ‡ฑ๐Ÿ‡ฆ", + "large_blue_circle": "๐Ÿ”ต", + "large_blue_diamond": "๐Ÿ”ท", + "large_orange_diamond": "๐Ÿ”ถ", + "last_quarter_moon": "๐ŸŒ—", + "last_quarter_moon_with_face": "๐ŸŒœ", + "latin_cross": "โœ๏ธ", + "latvia": "๐Ÿ‡ฑ๐Ÿ‡ป", + "laugh": "๐Ÿ˜‚", + "laughing": "๐Ÿ˜†", + "leafy_green": "๐Ÿฅฌ", + "leaves": "๐Ÿƒ", + "lebanon": "๐Ÿ‡ฑ๐Ÿ‡ง", + "ledger": "๐Ÿ“’", + "left_luggage": "๐Ÿ›…", + "left_right_arrow": "โ†”๏ธ", + "left_speech_bubble": "๐Ÿ—จ๏ธ", + "leftwards_arrow_with_hook": "โ†ฉ๏ธ", + "leftwards_hand": "๐Ÿซฒ", + "leftwards_pushing_hand": "๐Ÿซท", + "leg": "๐Ÿฆต", + "lemon": "๐Ÿ‹", + "leo": "โ™Œ", + "leopard": "๐Ÿ†", + "lesotho": "๐Ÿ‡ฑ๐Ÿ‡ธ", + "level_slider": "๐ŸŽš๏ธ", + "liberia": "๐Ÿ‡ฑ๐Ÿ‡ท", + "libra": "โ™Ž", + "libya": "๐Ÿ‡ฑ๐Ÿ‡พ", + "liechtenstein": "๐Ÿ‡ฑ๐Ÿ‡ฎ", + "light_blue_heart": "๐Ÿฉต", + "light_rail": "๐Ÿšˆ", + "like": "๐Ÿ‘", + "link": "๐Ÿ”—", + "lion": "๐Ÿฆ", + "lips": "๐Ÿ‘„", + "lipstick": "๐Ÿ’„", + "lithuania": "๐Ÿ‡ฑ๐Ÿ‡น", + "lizard": "๐ŸฆŽ", + "llama": "๐Ÿฆ™", + "lobster": "๐Ÿฆž", + "lock": "๐Ÿ”’", + "lock_with_ink_pen": "๐Ÿ”", + "lollipop": "๐Ÿญ", + "long_drum": "๐Ÿช˜", + "loop": "โžฟ", + "lotion_bottle": "๐Ÿงด", + "lotus": "๐Ÿชท", + "lotus_position": "๐Ÿง˜", + "lotus_position_man": "๐Ÿง˜โ€โ™‚๏ธ", + "lotus_position_woman": "๐Ÿง˜โ€โ™€๏ธ", + "loud_sound": "๐Ÿ”Š", + "loudspeaker": "๐Ÿ“ข", + "love_hotel": "๐Ÿฉ", + "love_letter": "๐Ÿ’Œ", + "love_you_gesture": "๐ŸคŸ", + "low_battery": "๐Ÿชซ", + "low_brightness": "๐Ÿ”…", + "luggage": "๐Ÿงณ", + "lungs": "๐Ÿซ", + "luxembourg": "๐Ÿ‡ฑ๐Ÿ‡บ", + "lying_face": "๐Ÿคฅ", + "m": "โ“‚๏ธ", + "macau": "๐Ÿ‡ฒ๐Ÿ‡ด", + "macedonia": "๐Ÿ‡ฒ๐Ÿ‡ฐ", + "madagascar": "๐Ÿ‡ฒ๐Ÿ‡ฌ", + "mag": "๐Ÿ”", + "mag_right": "๐Ÿ”Ž", + "mage": "๐Ÿง™", + "mage_man": "๐Ÿง™โ€โ™‚๏ธ", + "mage_woman": "๐Ÿง™โ€โ™€๏ธ", + "magic_wand": "๐Ÿช„", + "magnet": "๐Ÿงฒ", + "mahjong": "๐Ÿ€„", + "mailbox": "๐Ÿ“ซ", + "mailbox_closed": "๐Ÿ“ช", + "mailbox_with_mail": "๐Ÿ“ฌ", + "mailbox_with_no_mail": "๐Ÿ“ญ", + "malawi": "๐Ÿ‡ฒ๐Ÿ‡ผ", + "malaysia": "๐Ÿ‡ฒ๐Ÿ‡พ", + "maldives": "๐Ÿ‡ฒ๐Ÿ‡ป", + "male_detective": "๐Ÿ•ต๏ธโ€โ™‚๏ธ", + "male_sign": "โ™‚๏ธ", + "mali": "๐Ÿ‡ฒ๐Ÿ‡ฑ", + "malta": "๐Ÿ‡ฒ๐Ÿ‡น", + "mammoth": "๐Ÿฆฃ", + "man": "๐Ÿ‘จ", + "man_artist": "๐Ÿ‘จโ€๐ŸŽจ", + "man_astronaut": "๐Ÿ‘จโ€๐Ÿš€", + "man_beard": "๐Ÿง”โ€โ™‚๏ธ", + "man_cartwheeling": "๐Ÿคธโ€โ™‚๏ธ", + "man_cook": "๐Ÿ‘จโ€๐Ÿณ", + "man_dancing": "๐Ÿ•บ", + "man_facepalming": "๐Ÿคฆโ€โ™‚๏ธ", + "man_factory_worker": "๐Ÿ‘จโ€๐Ÿญ", + "man_farmer": "๐Ÿ‘จโ€๐ŸŒพ", + "man_feeding_baby": "๐Ÿ‘จโ€๐Ÿผ", + "man_firefighter": "๐Ÿ‘จโ€๐Ÿš’", + "man_health_worker": "๐Ÿ‘จโ€โš•๏ธ", + "man_in_manual_wheelchair": "๐Ÿ‘จโ€๐Ÿฆฝ", + "man_in_motorized_wheelchair": "๐Ÿ‘จโ€๐Ÿฆผ", + "man_in_tuxedo": "๐Ÿคตโ€โ™‚๏ธ", + "man_judge": "๐Ÿ‘จโ€โš–๏ธ", + "man_juggling": "๐Ÿคนโ€โ™‚๏ธ", + "man_mechanic": "๐Ÿ‘จโ€๐Ÿ”ง", + "man_office_worker": "๐Ÿ‘จโ€๐Ÿ’ผ", + "man_pilot": "๐Ÿ‘จโ€โœˆ๏ธ", + "man_playing_handball": "๐Ÿคพโ€โ™‚๏ธ", + "man_playing_water_polo": "๐Ÿคฝโ€โ™‚๏ธ", + "man_scientist": "๐Ÿ‘จโ€๐Ÿ”ฌ", + "man_shrugging": "๐Ÿคทโ€โ™‚๏ธ", + "man_singer": "๐Ÿ‘จโ€๐ŸŽค", + "man_student": "๐Ÿ‘จโ€๐ŸŽ“", + "man_teacher": "๐Ÿ‘จโ€๐Ÿซ", + "man_technologist": "๐Ÿ‘จโ€๐Ÿ’ป", + "man_with_gua_pi_mao": "๐Ÿ‘ฒ", + "man_with_probing_cane": "๐Ÿ‘จโ€๐Ÿฆฏ", + "man_with_turban": "๐Ÿ‘ณโ€โ™‚๏ธ", + "man_with_veil": "๐Ÿ‘ฐโ€โ™‚๏ธ", + "mandarin": "๐ŸŠ", + "mango": "๐Ÿฅญ", + "mans_shoe": "๐Ÿ‘ž", + "mantelpiece_clock": "๐Ÿ•ฐ๏ธ", + "manual_wheelchair": "๐Ÿฆฝ", + "maple_leaf": "๐Ÿ", + "maracas": "๐Ÿช‡", + "marshall_islands": "๐Ÿ‡ฒ๐Ÿ‡ญ", + "martial_arts_uniform": "๐Ÿฅ‹", + "martinique": "๐Ÿ‡ฒ๐Ÿ‡ถ", + "mask": "๐Ÿ˜ท", + "massage": "๐Ÿ’†", + "massage_man": "๐Ÿ’†โ€โ™‚๏ธ", + "massage_woman": "๐Ÿ’†โ€โ™€๏ธ", + "mate": "๐Ÿง‰", + "mauritania": "๐Ÿ‡ฒ๐Ÿ‡ท", + "mauritius": "๐Ÿ‡ฒ๐Ÿ‡บ", + "mayotte": "๐Ÿ‡พ๐Ÿ‡น", + "meat_on_bone": "๐Ÿ–", + "mechanic": "๐Ÿง‘โ€๐Ÿ”ง", + "mechanical_arm": "๐Ÿฆพ", + "mechanical_leg": "๐Ÿฆฟ", + "medal_military": "๐ŸŽ–๏ธ", + "medal_sports": "๐Ÿ…", + "medical_symbol": "โš•๏ธ", + "mega": "๐Ÿ“ฃ", + "melon": "๐Ÿˆ", + "melting_face": "๐Ÿซ ", + "memo": "๐Ÿ“", + "men_wrestling": "๐Ÿคผโ€โ™‚๏ธ", + "mending_heart": "โค๏ธโ€๐Ÿฉน", + "menorah": "๐Ÿ•Ž", + "mens": "๐Ÿšน", + "mermaid": "๐Ÿงœโ€โ™€๏ธ", + "merman": "๐Ÿงœโ€โ™‚๏ธ", + "merperson": "๐Ÿงœ", + "metal": "๐Ÿค˜", + "metro": "๐Ÿš‡", + "mexico": "๐Ÿ‡ฒ๐Ÿ‡ฝ", + "microbe": "๐Ÿฆ ", + "micronesia": "๐Ÿ‡ซ๐Ÿ‡ฒ", + "microphone": "๐ŸŽค", + "microscope": "๐Ÿ”ฌ", + "middle_finger": "๐Ÿ–•", + "military_helmet": "๐Ÿช–", + "milk_glass": "๐Ÿฅ›", + "milky_way": "๐ŸŒŒ", + "minibus": "๐Ÿš", + "minidisc": "๐Ÿ’ฝ", + "mirror": "๐Ÿชž", + "mirror_ball": "๐Ÿชฉ", + "mobile_phone_off": "๐Ÿ“ด", + "moldova": "๐Ÿ‡ฒ๐Ÿ‡ฉ", + "monaco": "๐Ÿ‡ฒ๐Ÿ‡จ", + "money_mouth_face": "๐Ÿค‘", + "money_with_wings": "๐Ÿ’ธ", + "moneybag": "๐Ÿ’ฐ", + "mongolia": "๐Ÿ‡ฒ๐Ÿ‡ณ", + "monkey": "๐Ÿ’", + "monkey_face": "๐Ÿต", + "monocle_face": "๐Ÿง", + "monorail": "๐Ÿš", + "montenegro": "๐Ÿ‡ฒ๐Ÿ‡ช", + "montserrat": "๐Ÿ‡ฒ๐Ÿ‡ธ", + "moon": "๐ŸŒ”", + "moon_cake": "๐Ÿฅฎ", + "moose": "๐ŸซŽ", + "morocco": "๐Ÿ‡ฒ๐Ÿ‡ฆ", + "mortar_board": "๐ŸŽ“", + "mosque": "๐Ÿ•Œ", + "mosquito": "๐ŸฆŸ", + "motor_boat": "๐Ÿ›ฅ๏ธ", + "motor_scooter": "๐Ÿ›ต", + "motorcycle": "๐Ÿ๏ธ", + "motorized_wheelchair": "๐Ÿฆผ", + "motorway": "๐Ÿ›ฃ๏ธ", + "mount_fuji": "๐Ÿ—ป", + "mountain": "โ›ฐ๏ธ", + "mountain_bicyclist": "๐Ÿšต", + "mountain_biking_man": "๐Ÿšตโ€โ™‚๏ธ", + "mountain_biking_woman": "๐Ÿšตโ€โ™€๏ธ", + "mountain_cableway": "๐Ÿš ", + "mountain_railway": "๐Ÿšž", + "mountain_snow": "๐Ÿ”๏ธ", + "mouse": "๐Ÿญ", + "mouse2": "๐Ÿ", + "mouse_trap": "๐Ÿชค", + "movie_camera": "๐ŸŽฅ", + "moyai": "๐Ÿ—ฟ", + "mozambique": "๐Ÿ‡ฒ๐Ÿ‡ฟ", + "mrs_claus": "๐Ÿคถ", + "muscle": "๐Ÿ’ช", + "mushroom": "๐Ÿ„", + "musical_keyboard": "๐ŸŽน", + "musical_note": "๐ŸŽต", + "musical_score": "๐ŸŽผ", + "mute": "๐Ÿ”‡", + "mx_claus": "๐Ÿง‘โ€๐ŸŽ„", + "myanmar": "๐Ÿ‡ฒ๐Ÿ‡ฒ", + "nail_care": "๐Ÿ’…", + "name_badge": "๐Ÿ“›", + "namibia": "๐Ÿ‡ณ๐Ÿ‡ฆ", + "national_park": "๐Ÿž๏ธ", + "nauru": "๐Ÿ‡ณ๐Ÿ‡ท", + "nauseated_face": "๐Ÿคข", + "nazar_amulet": "๐Ÿงฟ", + "necktie": "๐Ÿ‘”", + "negative_squared_cross_mark": "โŽ", + "nepal": "๐Ÿ‡ณ๐Ÿ‡ต", + "nerd_face": "๐Ÿค“", + "nest_with_eggs": "๐Ÿชบ", + "nesting_dolls": "๐Ÿช†", + "netherlands": "๐Ÿ‡ณ๐Ÿ‡ฑ", + "neutral_face": "๐Ÿ˜", + "new": "๐Ÿ†•", + "new_caledonia": "๐Ÿ‡ณ๐Ÿ‡จ", + "new_moon": "๐ŸŒ‘", + "new_moon_with_face": "๐ŸŒš", + "new_zealand": "๐Ÿ‡ณ๐Ÿ‡ฟ", + "newspaper": "๐Ÿ“ฐ", + "newspaper_roll": "๐Ÿ—ž๏ธ", + "next_track_button": "โญ๏ธ", + "ng": "๐Ÿ†–", + "ng_man": "๐Ÿ™…โ€โ™‚๏ธ", + "ng_woman": "๐Ÿ™…โ€โ™€๏ธ", + "nicaragua": "๐Ÿ‡ณ๐Ÿ‡ฎ", + "niger": "๐Ÿ‡ณ๐Ÿ‡ช", + "nigeria": "๐Ÿ‡ณ๐Ÿ‡ฌ", + "night_with_stars": "๐ŸŒƒ", + "nine": "9๏ธโƒฃ", + "ninja": "๐Ÿฅท", + "niue": "๐Ÿ‡ณ๐Ÿ‡บ", + "no_bell": "๐Ÿ”•", + "no_bicycles": "๐Ÿšณ", + "no_entry": "โ›”", + "no_entry_sign": "๐Ÿšซ", + "no_good": "๐Ÿ™…", + "no_good_man": "๐Ÿ™…โ€โ™‚๏ธ", + "no_good_woman": "๐Ÿ™…โ€โ™€๏ธ", + "no_mobile_phones": "๐Ÿ“ต", + "no_mouth": "๐Ÿ˜ถ", + "no_pedestrians": "๐Ÿšท", + "no_smoking": "๐Ÿšญ", + "non-potable_water": "๐Ÿšฑ", + "norfolk_island": "๐Ÿ‡ณ๐Ÿ‡ซ", + "north_korea": "๐Ÿ‡ฐ๐Ÿ‡ต", + "northern_mariana_islands": "๐Ÿ‡ฒ๐Ÿ‡ต", + "norway": "๐Ÿ‡ณ๐Ÿ‡ด", + "nose": "๐Ÿ‘ƒ", + "notebook": "๐Ÿ““", + "notebook_with_decorative_cover": "๐Ÿ“”", + "notes": "๐ŸŽถ", + "nut_and_bolt": "๐Ÿ”ฉ", + "o": "โญ•", + "o2": "๐Ÿ…พ๏ธ", + "ocean": "๐ŸŒŠ", + "octopus": "๐Ÿ™", + "oden": "๐Ÿข", + "office": "๐Ÿข", + "office_worker": "๐Ÿง‘โ€๐Ÿ’ผ", + "oil_drum": "๐Ÿ›ข๏ธ", + "ok": "๐Ÿ†—", + "ok_hand": "๐Ÿ‘Œ", + "ok_man": "๐Ÿ™†โ€โ™‚๏ธ", + "ok_person": "๐Ÿ™†", + "ok_woman": "๐Ÿ™†โ€โ™€๏ธ", + "old_key": "๐Ÿ—๏ธ", + "older_adult": "๐Ÿง“", + "older_man": "๐Ÿ‘ด", + "older_woman": "๐Ÿ‘ต", + "olive": "๐Ÿซ’", + "om": "๐Ÿ•‰๏ธ", + "oman": "๐Ÿ‡ด๐Ÿ‡ฒ", + "on": "๐Ÿ”›", + "oncoming_automobile": "๐Ÿš˜", + "oncoming_bus": "๐Ÿš", + "oncoming_police_car": "๐Ÿš”", + "oncoming_taxi": "๐Ÿš–", + "one": "1๏ธโƒฃ", + "one_piece_swimsuit": "๐Ÿฉฑ", + "onion": "๐Ÿง…", + "open_book": "๐Ÿ“–", + "open_file_folder": "๐Ÿ“‚", + "open_hands": "๐Ÿ‘", + "open_mouth": "๐Ÿ˜ฎ", + "open_umbrella": "โ˜‚๏ธ", + "ophiuchus": "โ›Ž", + "orange": "๐ŸŠ", + "orange_book": "๐Ÿ“™", + "orange_circle": "๐ŸŸ ", + "orange_heart": "๐Ÿงก", + "orange_square": "๐ŸŸง", + "orangutan": "๐Ÿฆง", + "orthodox_cross": "โ˜ฆ๏ธ", + "otter": "๐Ÿฆฆ", + "outbox_tray": "๐Ÿ“ค", + "owl": "๐Ÿฆ‰", + "ox": "๐Ÿ‚", + "oyster": "๐Ÿฆช", + "package": "๐Ÿ“ฆ", + "page_facing_up": "๐Ÿ“„", + "page_with_curl": "๐Ÿ“ƒ", + "pager": "๐Ÿ“Ÿ", + "paintbrush": "๐Ÿ–Œ๏ธ", + "pakistan": "๐Ÿ‡ต๐Ÿ‡ฐ", + "palau": "๐Ÿ‡ต๐Ÿ‡ผ", + "palestinian_territories": "๐Ÿ‡ต๐Ÿ‡ธ", + "palm_down_hand": "๐Ÿซณ", + "palm_tree": "๐ŸŒด", + "palm_up_hand": "๐Ÿซด", + "palms_up_together": "๐Ÿคฒ", + "panama": "๐Ÿ‡ต๐Ÿ‡ฆ", + "pancakes": "๐Ÿฅž", + "panda_face": "๐Ÿผ", + "paperclip": "๐Ÿ“Ž", + "paperclips": "๐Ÿ–‡๏ธ", + "papua_new_guinea": "๐Ÿ‡ต๐Ÿ‡ฌ", + "parachute": "๐Ÿช‚", + "paraguay": "๐Ÿ‡ต๐Ÿ‡พ", + "parasol_on_ground": "โ›ฑ๏ธ", + "parking": "๐Ÿ…ฟ๏ธ", + "parrot": "๐Ÿฆœ", + "part_alternation_mark": "ใ€ฝ๏ธ", + "partly_sunny": "โ›…", + "party": "๐ŸŽ‰", + "partying_face": "๐Ÿฅณ", + "passenger_ship": "๐Ÿ›ณ๏ธ", + "passport_control": "๐Ÿ›‚", + "pause_button": "โธ๏ธ", + "paw_prints": "๐Ÿพ", + "pea_pod": "๐Ÿซ›", + "peace_symbol": "โ˜ฎ๏ธ", + "peach": "๐Ÿ‘", + "peacock": "๐Ÿฆš", + "peanuts": "๐Ÿฅœ", + "pear": "๐Ÿ", + "pen": "๐Ÿ–Š๏ธ", + "pencil": "๐Ÿ“", + "pencil2": "โœ๏ธ", + "penguin": "๐Ÿง", + "pensive": "๐Ÿ˜”", + "people_holding_hands": "๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘", + "people_hugging": "๐Ÿซ‚", + "performing_arts": "๐ŸŽญ", + "persevere": "๐Ÿ˜ฃ", + "person_bald": "๐Ÿง‘โ€๐Ÿฆฒ", + "person_curly_hair": "๐Ÿง‘โ€๐Ÿฆฑ", + "person_feeding_baby": "๐Ÿง‘โ€๐Ÿผ", + "person_fencing": "๐Ÿคบ", + "person_in_manual_wheelchair": "๐Ÿง‘โ€๐Ÿฆฝ", + "person_in_motorized_wheelchair": "๐Ÿง‘โ€๐Ÿฆผ", + "person_in_tuxedo": "๐Ÿคต", + "person_red_hair": "๐Ÿง‘โ€๐Ÿฆฐ", + "person_white_hair": "๐Ÿง‘โ€๐Ÿฆณ", + "person_with_crown": "๐Ÿซ…", + "person_with_probing_cane": "๐Ÿง‘โ€๐Ÿฆฏ", + "person_with_turban": "๐Ÿ‘ณ", + "person_with_veil": "๐Ÿ‘ฐ", + "peru": "๐Ÿ‡ต๐Ÿ‡ช", + "petri_dish": "๐Ÿงซ", + "philippines": "๐Ÿ‡ต๐Ÿ‡ญ", + "phone": "โ˜Ž๏ธ", + "pick": "โ›๏ธ", + "pickup_truck": "๐Ÿ›ป", + "pie": "๐Ÿฅง", + "pig": "๐Ÿท", + "pig2": "๐Ÿ–", + "pig_nose": "๐Ÿฝ", + "pill": "๐Ÿ’Š", + "pilot": "๐Ÿง‘โ€โœˆ๏ธ", + "pinata": "๐Ÿช…", + "pinched_fingers": "๐ŸคŒ", + "pinching_hand": "๐Ÿค", + "pineapple": "๐Ÿ", + "ping_pong": "๐Ÿ“", + "pink_heart": "๐Ÿฉท", + "pirate_flag": "๐Ÿดโ€โ˜ ๏ธ", + "pisces": "โ™“", + "pitcairn_islands": "๐Ÿ‡ต๐Ÿ‡ณ", + "pizza": "๐Ÿ•", + "placard": "๐Ÿชง", + "place_of_worship": "๐Ÿ›", + "plate_with_cutlery": "๐Ÿฝ๏ธ", + "play_or_pause_button": "โฏ๏ธ", + "playground_slide": "๐Ÿ›", + "pleading_face": "๐Ÿฅบ", + "plunger": "๐Ÿช ", + "point_down": "๐Ÿ‘‡", + "point_left": "๐Ÿ‘ˆ", + "point_right": "๐Ÿ‘‰", + "point_up": "โ˜๏ธ", + "point_up_2": "๐Ÿ‘†", + "poland": "๐Ÿ‡ต๐Ÿ‡ฑ", + "polar_bear": "๐Ÿปโ€โ„๏ธ", + "police_car": "๐Ÿš“", + "police_officer": "๐Ÿ‘ฎ", + "policeman": "๐Ÿ‘ฎโ€โ™‚๏ธ", + "policewoman": "๐Ÿ‘ฎโ€โ™€๏ธ", + "poodle": "๐Ÿฉ", + "poop": "๐Ÿ’ฉ", + "popcorn": "๐Ÿฟ", + "portugal": "๐Ÿ‡ต๐Ÿ‡น", + "post_office": "๐Ÿฃ", + "postal_horn": "๐Ÿ“ฏ", + "postbox": "๐Ÿ“ฎ", + "potable_water": "๐Ÿšฐ", + "potato": "๐Ÿฅ”", + "potted_plant": "๐Ÿชด", + "pouch": "๐Ÿ‘", + "poultry_leg": "๐Ÿ—", + "pound": "๐Ÿ’ท", + "pouring_liquid": "๐Ÿซ—", + "pout": "๐Ÿ˜ก", + "pouting_cat": "๐Ÿ˜พ", + "pouting_face": "๐Ÿ™Ž", + "pouting_man": "๐Ÿ™Žโ€โ™‚๏ธ", + "pouting_woman": "๐Ÿ™Žโ€โ™€๏ธ", + "pray": "๐Ÿ™", + "prayer_beads": "๐Ÿ“ฟ", + "pregnant_man": "๐Ÿซƒ", + "pregnant_person": "๐Ÿซ„", + "pregnant_woman": "๐Ÿคฐ", + "pretzel": "๐Ÿฅจ", + "previous_track_button": "โฎ๏ธ", + "prince": "๐Ÿคด", + "princess": "๐Ÿ‘ธ", + "printer": "๐Ÿ–จ๏ธ", + "probing_cane": "๐Ÿฆฏ", + "puerto_rico": "๐Ÿ‡ต๐Ÿ‡ท", + "puke": "๐Ÿคฎ", + "punch": "๐Ÿ‘Š", + "purple_circle": "๐ŸŸฃ", + "purple_heart": "๐Ÿ’œ", + "purple_square": "๐ŸŸช", + "purse": "๐Ÿ‘›", + "pushpin": "๐Ÿ“Œ", + "put_litter_in_its_place": "๐Ÿšฎ", + "qatar": "๐Ÿ‡ถ๐Ÿ‡ฆ", + "question": "โ“", + "rabbit": "๐Ÿฐ", + "rabbit2": "๐Ÿ‡", + "raccoon": "๐Ÿฆ", + "racehorse": "๐ŸŽ", + "racing_car": "๐ŸŽ๏ธ", + "radio": "๐Ÿ“ป", + "radio_button": "๐Ÿ”˜", + "radioactive": "โ˜ข๏ธ", + "rage": "๐Ÿ˜ก", + "railway_car": "๐Ÿšƒ", + "railway_track": "๐Ÿ›ค๏ธ", + "rainbow": "๐ŸŒˆ", + "rainbow_flag": "๐Ÿณ๏ธโ€๐ŸŒˆ", + "raised_back_of_hand": "๐Ÿคš", + "raised_eyebrow": "๐Ÿคจ", + "raised_hand": "โœ‹", + "raised_hand_with_fingers_splayed": "๐Ÿ–๏ธ", + "raised_hands": "๐Ÿ™Œ", + "raising_hand": "๐Ÿ™‹", + "raising_hand_man": "๐Ÿ™‹โ€โ™‚๏ธ", + "raising_hand_woman": "๐Ÿ™‹โ€โ™€๏ธ", + "ram": "๐Ÿ", + "ramen": "๐Ÿœ", + "rat": "๐Ÿ€", + "razor": "๐Ÿช’", + "receipt": "๐Ÿงพ", + "record_button": "โบ๏ธ", + "recycle": "โ™ป๏ธ", + "red_car": "๐Ÿš—", + "red_circle": "๐Ÿ”ด", + "red_envelope": "๐Ÿงง", + "red_haired_man": "๐Ÿ‘จโ€๐Ÿฆฐ", + "red_haired_woman": "๐Ÿ‘ฉโ€๐Ÿฆฐ", + "red_square": "๐ŸŸฅ", + "registered": "ยฎ๏ธ", + "relaxed": "โ˜บ๏ธ", + "relieved": "๐Ÿ˜Œ", + "reminder_ribbon": "๐ŸŽ—๏ธ", + "repeat": "๐Ÿ”", + "repeat_one": "๐Ÿ”‚", + "rescue_worker_helmet": "โ›‘๏ธ", + "restroom": "๐Ÿšป", + "reunion": "๐Ÿ‡ท๐Ÿ‡ช", + "revolving_hearts": "๐Ÿ’ž", + "rewind": "โช", + "rhinoceros": "๐Ÿฆ", + "ribbon": "๐ŸŽ€", + "rice": "๐Ÿš", + "rice_ball": "๐Ÿ™", + "rice_cracker": "๐Ÿ˜", + "rice_scene": "๐ŸŽ‘", + "right_anger_bubble": "๐Ÿ—ฏ๏ธ", + "rightwards_hand": "๐Ÿซฑ", + "rightwards_pushing_hand": "๐Ÿซธ", + "ring": "๐Ÿ’", + "ring_buoy": "๐Ÿ›Ÿ", + "ringed_planet": "๐Ÿช", + "robot": "๐Ÿค–", + "rock": "๐Ÿชจ", + "rocket": "๐Ÿš€", + "rofl": "๐Ÿคฃ", + "roll_eyes": "๐Ÿ™„", + "roll_of_paper": "๐Ÿงป", + "roller_coaster": "๐ŸŽข", + "roller_skate": "๐Ÿ›ผ", + "romania": "๐Ÿ‡ท๐Ÿ‡ด", + "rooster": "๐Ÿ“", + "rose": "๐ŸŒน", + "rosette": "๐Ÿต๏ธ", + "rotating_light": "๐Ÿšจ", + "round_pushpin": "๐Ÿ“", + "rowboat": "๐Ÿšฃ", + "rowing_man": "๐Ÿšฃโ€โ™‚๏ธ", + "rowing_woman": "๐Ÿšฃโ€โ™€๏ธ", + "ru": "๐Ÿ‡ท๐Ÿ‡บ", + "rugby_football": "๐Ÿ‰", + "runner": "๐Ÿƒ", + "running": "๐Ÿƒ", + "running_man": "๐Ÿƒโ€โ™‚๏ธ", + "running_shirt_with_sash": "๐ŸŽฝ", + "running_woman": "๐Ÿƒโ€โ™€๏ธ", + "rwanda": "๐Ÿ‡ท๐Ÿ‡ผ", + "sa": "๐Ÿˆ‚๏ธ", + "sad": "๐Ÿ˜ข", + "safety_pin": "๐Ÿงท", + "safety_vest": "๐Ÿฆบ", + "sagittarius": "โ™", + "sailboat": "โ›ต", + "sake": "๐Ÿถ", + "salt": "๐Ÿง‚", + "saluting_face": "๐Ÿซก", + "samoa": "๐Ÿ‡ผ๐Ÿ‡ธ", + "san_marino": "๐Ÿ‡ธ๐Ÿ‡ฒ", + "sandal": "๐Ÿ‘ก", + "sandwich": "๐Ÿฅช", + "santa": "๐ŸŽ…", + "sao_tome_principe": "๐Ÿ‡ธ๐Ÿ‡น", + "sari": "๐Ÿฅป", + "sassy_man": "๐Ÿ’โ€โ™‚๏ธ", + "sassy_woman": "๐Ÿ’โ€โ™€๏ธ", + "satellite": "๐Ÿ“ก", + "satisfied": "๐Ÿ˜†", + "saudi_arabia": "๐Ÿ‡ธ๐Ÿ‡ฆ", + "sauna_man": "๐Ÿง–โ€โ™‚๏ธ", + "sauna_person": "๐Ÿง–", + "sauna_woman": "๐Ÿง–โ€โ™€๏ธ", + "sauropod": "๐Ÿฆ•", + "saxophone": "๐ŸŽท", + "scarf": "๐Ÿงฃ", + "school": "๐Ÿซ", + "school_satchel": "๐ŸŽ’", + "scientist": "๐Ÿง‘โ€๐Ÿ”ฌ", + "scissors": "โœ‚๏ธ", + "scorpion": "๐Ÿฆ‚", + "scorpius": "โ™", + "scotland": "๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ", + "scream": "๐Ÿ˜ฑ", + "scream_cat": "๐Ÿ™€", + "screwdriver": "๐Ÿช›", + "scroll": "๐Ÿ“œ", + "seal": "๐Ÿฆญ", + "seat": "๐Ÿ’บ", + "secret": "ใŠ™๏ธ", + "see_no_evil": "๐Ÿ™ˆ", + "seedling": "๐ŸŒฑ", + "selfie": "๐Ÿคณ", + "senegal": "๐Ÿ‡ธ๐Ÿ‡ณ", + "serbia": "๐Ÿ‡ท๐Ÿ‡ธ", + "service_dog": "๐Ÿ•โ€๐Ÿฆบ", + "seven": "7๏ธโƒฃ", + "sewing_needle": "๐Ÿชก", + "seychelles": "๐Ÿ‡ธ๐Ÿ‡จ", + "shaking_face": "๐Ÿซจ", + "shallow_pan_of_food": "๐Ÿฅ˜", + "shamrock": "โ˜˜๏ธ", + "shark": "๐Ÿฆˆ", + "shaved_ice": "๐Ÿง", + "sheep": "๐Ÿ‘", + "shell": "๐Ÿš", + "shield": "๐Ÿ›ก๏ธ", + "shinto_shrine": "โ›ฉ๏ธ", + "ship": "๐Ÿšข", + "shirt": "๐Ÿ‘•", + "shit": "๐Ÿ’ฉ", + "shoe": "๐Ÿ‘ž", + "shopping": "๐Ÿ›๏ธ", + "shopping_cart": "๐Ÿ›’", + "shorts": "๐Ÿฉณ", + "shower": "๐Ÿšฟ", + "shrimp": "๐Ÿฆ", + "shrug": "๐Ÿคท", + "shushing_face": "๐Ÿคซ", + "sierra_leone": "๐Ÿ‡ธ๐Ÿ‡ฑ", + "signal_strength": "๐Ÿ“ถ", + "singapore": "๐Ÿ‡ธ๐Ÿ‡ฌ", + "singer": "๐Ÿง‘โ€๐ŸŽค", + "sint_maarten": "๐Ÿ‡ธ๐Ÿ‡ฝ", + "six": "6๏ธโƒฃ", + "six_pointed_star": "๐Ÿ”ฏ", + "skateboard": "๐Ÿ›น", + "ski": "๐ŸŽฟ", + "skier": "โ›ท๏ธ", + "skull": "๐Ÿ’€", + "skull_and_crossbones": "โ˜ ๏ธ", + "skunk": "๐Ÿฆจ", + "sled": "๐Ÿ›ท", + "sleeping": "๐Ÿ˜ด", + "sleeping_bed": "๐Ÿ›Œ", + "sleepy": "๐Ÿ˜ช", + "slightly_frowning_face": "๐Ÿ™", + "slightly_smiling_face": "๐Ÿ™‚", + "slot_machine": "๐ŸŽฐ", + "sloth": "๐Ÿฆฅ", + "slovakia": "๐Ÿ‡ธ๐Ÿ‡ฐ", + "slovenia": "๐Ÿ‡ธ๐Ÿ‡ฎ", + "small_airplane": "๐Ÿ›ฉ๏ธ", + "small_blue_diamond": "๐Ÿ”น", + "small_orange_diamond": "๐Ÿ”ธ", + "small_red_triangle": "๐Ÿ”บ", + "small_red_triangle_down": "๐Ÿ”ป", + "smile": "๐Ÿ˜„", + "smile_cat": "๐Ÿ˜ธ", + "smiley": "๐Ÿ˜ƒ", + "smiley_cat": "๐Ÿ˜บ", + "smiling_face_with_tear": "๐Ÿฅฒ", + "smiling_face_with_three_hearts": "๐Ÿฅฐ", + "smiling_imp": "๐Ÿ˜ˆ", + "smirk": "๐Ÿ˜", + "smirk_cat": "๐Ÿ˜ผ", + "smoking": "๐Ÿšฌ", + "snail": "๐ŸŒ", + "snake": "๐Ÿ", + "sneezing_face": "๐Ÿคง", + "snowboarder": "๐Ÿ‚", + "snowflake": "โ„๏ธ", + "snowman": "โ›„", + "snowman_with_snow": "โ˜ƒ๏ธ", + "soap": "๐Ÿงผ", + "sob": "๐Ÿ˜ญ", + "soccer": "โšฝ", + "socks": "๐Ÿงฆ", + "softball": "๐ŸฅŽ", + "solomon_islands": "๐Ÿ‡ธ๐Ÿ‡ง", + "somalia": "๐Ÿ‡ธ๐Ÿ‡ด", + "soon": "๐Ÿ”œ", + "sos": "๐Ÿ†˜", + "sound": "๐Ÿ”‰", + "south_africa": "๐Ÿ‡ฟ๐Ÿ‡ฆ", + "south_georgia_south_sandwich_islands": "๐Ÿ‡ฌ๐Ÿ‡ธ", + "south_sudan": "๐Ÿ‡ธ๐Ÿ‡ธ", + "space_invader": "๐Ÿ‘พ", + "spades": "โ™ ๏ธ", + "spaghetti": "๐Ÿ", + "sparkle": "โ‡๏ธ", + "sparkler": "๐ŸŽ‡", + "sparkles": "โœจ", + "sparkling_heart": "๐Ÿ’–", + "speak_no_evil": "๐Ÿ™Š", + "speaker": "๐Ÿ”ˆ", + "speaking_head": "๐Ÿ—ฃ๏ธ", + "speech_balloon": "๐Ÿ’ฌ", + "speedboat": "๐Ÿšค", + "spider": "๐Ÿ•ท๏ธ", + "spider_web": "๐Ÿ•ธ๏ธ", + "spiral_calendar": "๐Ÿ—“๏ธ", + "spiral_notepad": "๐Ÿ—’๏ธ", + "sponge": "๐Ÿงฝ", + "spoon": "๐Ÿฅ„", + "squid": "๐Ÿฆ‘", + "sri_lanka": "๐Ÿ‡ฑ๐Ÿ‡ฐ", + "st_barthelemy": "๐Ÿ‡ง๐Ÿ‡ฑ", + "st_helena": "๐Ÿ‡ธ๐Ÿ‡ญ", + "st_kitts_nevis": "๐Ÿ‡ฐ๐Ÿ‡ณ", + "st_lucia": "๐Ÿ‡ฑ๐Ÿ‡จ", + "st_martin": "๐Ÿ‡ฒ๐Ÿ‡ซ", + "st_pierre_miquelon": "๐Ÿ‡ต๐Ÿ‡ฒ", + "st_vincent_grenadines": "๐Ÿ‡ป๐Ÿ‡จ", + "stadium": "๐ŸŸ๏ธ", + "standing_man": "๐Ÿงโ€โ™‚๏ธ", + "standing_person": "๐Ÿง", + "standing_woman": "๐Ÿงโ€โ™€๏ธ", + "star": "โญ", + "star2": "๐ŸŒŸ", + "star_and_crescent": "โ˜ช๏ธ", + "star_of_david": "โœก๏ธ", + "star_struck": "๐Ÿคฉ", + "stars": "๐ŸŒ ", + "station": "๐Ÿš‰", + "statue_of_liberty": "๐Ÿ—ฝ", + "steam_locomotive": "๐Ÿš‚", + "stethoscope": "๐Ÿฉบ", + "stew": "๐Ÿฒ", + "stop_button": "โน๏ธ", + "stop_sign": "๐Ÿ›‘", + "stopwatch": "โฑ๏ธ", + "straight_ruler": "๐Ÿ“", + "strawberry": "๐Ÿ“", + "stuck_out_tongue": "๐Ÿ˜›", + "stuck_out_tongue_closed_eyes": "๐Ÿ˜", + "stuck_out_tongue_winking_eye": "๐Ÿ˜œ", + "student": "๐Ÿง‘โ€๐ŸŽ“", + "studio_microphone": "๐ŸŽ™๏ธ", + "stuffed_flatbread": "๐Ÿฅ™", + "sudan": "๐Ÿ‡ธ๐Ÿ‡ฉ", + "sun_behind_large_cloud": "๐ŸŒฅ๏ธ", + "sun_behind_rain_cloud": "๐ŸŒฆ๏ธ", + "sun_behind_small_cloud": "๐ŸŒค๏ธ", + "sun_with_face": "๐ŸŒž", + "sunflower": "๐ŸŒป", + "sunglasses": "๐Ÿ˜Ž", + "sunny": "โ˜€๏ธ", + "sunrise": "๐ŸŒ…", + "sunrise_over_mountains": "๐ŸŒ„", + "superhero": "๐Ÿฆธ", + "superhero_man": "๐Ÿฆธโ€โ™‚๏ธ", + "superhero_woman": "๐Ÿฆธโ€โ™€๏ธ", + "supervillain": "๐Ÿฆน", + "supervillain_man": "๐Ÿฆนโ€โ™‚๏ธ", + "supervillain_woman": "๐Ÿฆนโ€โ™€๏ธ", + "surfer": "๐Ÿ„", + "surfing_man": "๐Ÿ„โ€โ™‚๏ธ", + "surfing_woman": "๐Ÿ„โ€โ™€๏ธ", + "suriname": "๐Ÿ‡ธ๐Ÿ‡ท", + "sushi": "๐Ÿฃ", + "suspension_railway": "๐ŸšŸ", + "svalbard_jan_mayen": "๐Ÿ‡ธ๐Ÿ‡ฏ", + "swan": "๐Ÿฆข", + "swaziland": "๐Ÿ‡ธ๐Ÿ‡ฟ", + "sweat": "๐Ÿ˜“", + "sweat_drops": "๐Ÿ’ฆ", + "sweat_smile": "๐Ÿ˜…", + "sweden": "๐Ÿ‡ธ๐Ÿ‡ช", + "sweet_potato": "๐Ÿ ", + "swim_brief": "๐Ÿฉฒ", + "swimmer": "๐ŸŠ", + "swimming_man": "๐ŸŠโ€โ™‚๏ธ", + "swimming_woman": "๐ŸŠโ€โ™€๏ธ", + "switzerland": "๐Ÿ‡จ๐Ÿ‡ญ", + "symbols": "๐Ÿ”ฃ", + "synagogue": "๐Ÿ•", + "syria": "๐Ÿ‡ธ๐Ÿ‡พ", + "syringe": "๐Ÿ’‰", + "t-rex": "๐Ÿฆ–", + "taco": "๐ŸŒฎ", + "tada": "๐ŸŽ‰", + "taiwan": "๐Ÿ‡น๐Ÿ‡ผ", + "tajikistan": "๐Ÿ‡น๐Ÿ‡ฏ", + "takeout_box": "๐Ÿฅก", + "tamale": "๐Ÿซ”", + "tanabata_tree": "๐ŸŽ‹", + "tangerine": "๐ŸŠ", + "tanzania": "๐Ÿ‡น๐Ÿ‡ฟ", + "taurus": "โ™‰", + "taxi": "๐Ÿš•", + "tea": "๐Ÿต", + "teacher": "๐Ÿง‘โ€๐Ÿซ", + "teapot": "๐Ÿซ–", + "technologist": "๐Ÿง‘โ€๐Ÿ’ป", + "teddy_bear": "๐Ÿงธ", + "telephone": "โ˜Ž๏ธ", + "telephone_receiver": "๐Ÿ“ž", + "telescope": "๐Ÿ”ญ", + "tennis": "๐ŸŽพ", + "tent": "โ›บ", + "test_tube": "๐Ÿงช", + "thailand": "๐Ÿ‡น๐Ÿ‡ญ", + "thermometer": "๐ŸŒก๏ธ", + "thinking": "๐Ÿค”", + "thong_sandal": "๐Ÿฉด", + "thought_balloon": "๐Ÿ’ญ", + "thread": "๐Ÿงต", + "three": "3๏ธโƒฃ", + "thumbsdown": "๐Ÿ‘Ž", + "thumbsup": "๐Ÿ‘", + "ticket": "๐ŸŽซ", + "tickets": "๐ŸŽŸ๏ธ", + "tiger": "๐Ÿฏ", + "tiger2": "๐Ÿ…", + "timer_clock": "โฒ๏ธ", + "timor_leste": "๐Ÿ‡น๐Ÿ‡ฑ", + "tipping_hand_man": "๐Ÿ’โ€โ™‚๏ธ", + "tipping_hand_person": "๐Ÿ’", + "tipping_hand_woman": "๐Ÿ’โ€โ™€๏ธ", + "tired_face": "๐Ÿ˜ซ", + "tm": "โ„ข๏ธ", + "togo": "๐Ÿ‡น๐Ÿ‡ฌ", + "toilet": "๐Ÿšฝ", + "tokelau": "๐Ÿ‡น๐Ÿ‡ฐ", + "tokyo_tower": "๐Ÿ—ผ", + "tomato": "๐Ÿ…", + "tonga": "๐Ÿ‡น๐Ÿ‡ด", + "tongue": "๐Ÿ‘…", + "toolbox": "๐Ÿงฐ", + "tooth": "๐Ÿฆท", + "toothbrush": "๐Ÿชฅ", + "top": "๐Ÿ”", + "tophat": "๐ŸŽฉ", + "tornado": "๐ŸŒช๏ธ", + "tr": "๐Ÿ‡น๐Ÿ‡ท", + "trackball": "๐Ÿ–ฒ๏ธ", + "tractor": "๐Ÿšœ", + "traffic_light": "๐Ÿšฅ", + "train": "๐Ÿš‹", + "train2": "๐Ÿš†", + "tram": "๐ŸšŠ", + "transgender_flag": "๐Ÿณ๏ธโ€โšง๏ธ", + "transgender_symbol": "โšง๏ธ", + "triangular_flag_on_post": "๐Ÿšฉ", + "triangular_ruler": "๐Ÿ“", + "trident": "๐Ÿ”ฑ", + "trinidad_tobago": "๐Ÿ‡น๐Ÿ‡น", + "tristan_da_cunha": "๐Ÿ‡น๐Ÿ‡ฆ", + "triumph": "๐Ÿ˜ค", + "troll": "๐ŸงŒ", + "trolleybus": "๐ŸšŽ", + "trophy": "๐Ÿ†", + "tropical_drink": "๐Ÿน", + "tropical_fish": "๐Ÿ ", + "truck": "๐Ÿšš", + "trumpet": "๐ŸŽบ", + "tshirt": "๐Ÿ‘•", + "tulip": "๐ŸŒท", + "tumbler_glass": "๐Ÿฅƒ", + "tunisia": "๐Ÿ‡น๐Ÿ‡ณ", + "turkey": "๐Ÿฆƒ", + "turkmenistan": "๐Ÿ‡น๐Ÿ‡ฒ", + "turks_caicos_islands": "๐Ÿ‡น๐Ÿ‡จ", + "turtle": "๐Ÿข", + "tuvalu": "๐Ÿ‡น๐Ÿ‡ป", + "tv": "๐Ÿ“บ", + "twisted_rightwards_arrows": "๐Ÿ”€", + "two": "2๏ธโƒฃ", + "two_hearts": "๐Ÿ’•", + "two_men_holding_hands": "๐Ÿ‘ฌ", + "two_women_holding_hands": "๐Ÿ‘ญ", + "u5272": "๐Ÿˆน", + "u5408": "๐Ÿˆด", + "u55b6": "๐Ÿˆบ", + "u6307": "๐Ÿˆฏ", + "u6708": "๐Ÿˆท๏ธ", + "u6709": "๐Ÿˆถ", + "u6e80": "๐Ÿˆต", + "u7121": "๐Ÿˆš", + "u7533": "๐Ÿˆธ", + "u7981": "๐Ÿˆฒ", + "u7a7a": "๐Ÿˆณ", + "uganda": "๐Ÿ‡บ๐Ÿ‡ฌ", + "uk": "๐Ÿ‡ฌ๐Ÿ‡ง", + "ukraine": "๐Ÿ‡บ๐Ÿ‡ฆ", + "umbrella": "โ˜”", + "unamused": "๐Ÿ˜’", + "underage": "๐Ÿ”ž", + "unicorn": "๐Ÿฆ„", + "united_arab_emirates": "๐Ÿ‡ฆ๐Ÿ‡ช", + "united_nations": "๐Ÿ‡บ๐Ÿ‡ณ", + "unlock": "๐Ÿ”“", + "up": "๐Ÿ†™", + "upside_down_face": "๐Ÿ™ƒ", + "uruguay": "๐Ÿ‡บ๐Ÿ‡พ", + "us": "๐Ÿ‡บ๐Ÿ‡ธ", + "us_outlying_islands": "๐Ÿ‡บ๐Ÿ‡ฒ", + "us_virgin_islands": "๐Ÿ‡ป๐Ÿ‡ฎ", + "uzbekistan": "๐Ÿ‡บ๐Ÿ‡ฟ", + "v": "โœŒ๏ธ", + "vampire": "๐Ÿง›", + "vampire_man": "๐Ÿง›โ€โ™‚๏ธ", + "vampire_woman": "๐Ÿง›โ€โ™€๏ธ", + "vanuatu": "๐Ÿ‡ป๐Ÿ‡บ", + "vatican_city": "๐Ÿ‡ป๐Ÿ‡ฆ", + "venezuela": "๐Ÿ‡ป๐Ÿ‡ช", + "vertical_traffic_light": "๐Ÿšฆ", + "vhs": "๐Ÿ“ผ", + "vibration_mode": "๐Ÿ“ณ", + "video_camera": "๐Ÿ“น", + "video_game": "๐ŸŽฎ", + "vietnam": "๐Ÿ‡ป๐Ÿ‡ณ", + "violin": "๐ŸŽป", + "virgo": "โ™", + "volcano": "๐ŸŒ‹", + "volleyball": "๐Ÿ", + "vomiting_face": "๐Ÿคฎ", + "vs": "๐Ÿ†š", + "vulcan_salute": "๐Ÿ––", + "waffle": "๐Ÿง‡", + "wales": "๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ", + "walking": "๐Ÿšถ", + "walking_man": "๐Ÿšถโ€โ™‚๏ธ", + "walking_woman": "๐Ÿšถโ€โ™€๏ธ", + "wallis_futuna": "๐Ÿ‡ผ๐Ÿ‡ซ", + "waning_crescent_moon": "๐ŸŒ˜", + "waning_gibbous_moon": "๐ŸŒ–", + "warning": "โš ๏ธ", + "wastebasket": "๐Ÿ—‘๏ธ", + "watch": "โŒš", + "water_buffalo": "๐Ÿƒ", + "water_polo": "๐Ÿคฝ", + "watermelon": "๐Ÿ‰", + "wave": "๐Ÿ‘‹", + "wavy_dash": "ใ€ฐ๏ธ", + "waxing_crescent_moon": "๐ŸŒ’", + "waxing_gibbous_moon": "๐ŸŒ”", + "wc": "๐Ÿšพ", + "weary": "๐Ÿ˜ฉ", + "wedding": "๐Ÿ’’", + "weight_lifting": "๐Ÿ‹๏ธ", + "weight_lifting_man": "๐Ÿ‹๏ธโ€โ™‚๏ธ", + "weight_lifting_woman": "๐Ÿ‹๏ธโ€โ™€๏ธ", + "western_sahara": "๐Ÿ‡ช๐Ÿ‡ญ", + "whale": "๐Ÿณ", + "whale2": "๐Ÿ‹", + "wheel": "๐Ÿ›ž", + "wheel_of_dharma": "โ˜ธ๏ธ", + "wheelchair": "โ™ฟ", + "white_check_mark": "โœ…", + "white_circle": "โšช", + "white_flag": "๐Ÿณ๏ธ", + "white_flower": "๐Ÿ’ฎ", + "white_haired_man": "๐Ÿ‘จโ€๐Ÿฆณ", + "white_haired_woman": "๐Ÿ‘ฉโ€๐Ÿฆณ", + "white_heart": "๐Ÿค", + "white_large_square": "โฌœ", + "white_medium_small_square": "โ—ฝ", + "white_medium_square": "โ—ป๏ธ", + "white_small_square": "โ–ซ๏ธ", + "white_square_button": "๐Ÿ”ณ", + "wilted_flower": "๐Ÿฅ€", + "wind_chime": "๐ŸŽ", + "wind_face": "๐ŸŒฌ๏ธ", + "window": "๐ŸชŸ", + "wine_glass": "๐Ÿท", + "wing": "๐Ÿชฝ", + "wink": "๐Ÿ˜‰", + "wireless": "๐Ÿ›œ", + "wolf": "๐Ÿบ", + "woman": "๐Ÿ‘ฉ", + "woman_artist": "๐Ÿ‘ฉโ€๐ŸŽจ", + "woman_astronaut": "๐Ÿ‘ฉโ€๐Ÿš€", + "woman_beard": "๐Ÿง”โ€โ™€๏ธ", + "woman_cartwheeling": "๐Ÿคธโ€โ™€๏ธ", + "woman_cook": "๐Ÿ‘ฉโ€๐Ÿณ", + "woman_dancing": "๐Ÿ’ƒ", + "woman_facepalming": "๐Ÿคฆโ€โ™€๏ธ", + "woman_factory_worker": "๐Ÿ‘ฉโ€๐Ÿญ", + "woman_farmer": "๐Ÿ‘ฉโ€๐ŸŒพ", + "woman_feeding_baby": "๐Ÿ‘ฉโ€๐Ÿผ", + "woman_firefighter": "๐Ÿ‘ฉโ€๐Ÿš’", + "woman_health_worker": "๐Ÿ‘ฉโ€โš•๏ธ", + "woman_in_manual_wheelchair": "๐Ÿ‘ฉโ€๐Ÿฆฝ", + "woman_in_motorized_wheelchair": "๐Ÿ‘ฉโ€๐Ÿฆผ", + "woman_in_tuxedo": "๐Ÿคตโ€โ™€๏ธ", + "woman_judge": "๐Ÿ‘ฉโ€โš–๏ธ", + "woman_juggling": "๐Ÿคนโ€โ™€๏ธ", + "woman_mechanic": "๐Ÿ‘ฉโ€๐Ÿ”ง", + "woman_office_worker": "๐Ÿ‘ฉโ€๐Ÿ’ผ", + "woman_pilot": "๐Ÿ‘ฉโ€โœˆ๏ธ", + "woman_playing_handball": "๐Ÿคพโ€โ™€๏ธ", + "woman_playing_water_polo": "๐Ÿคฝโ€โ™€๏ธ", + "woman_scientist": "๐Ÿ‘ฉโ€๐Ÿ”ฌ", + "woman_shrugging": "๐Ÿคทโ€โ™€๏ธ", + "woman_singer": "๐Ÿ‘ฉโ€๐ŸŽค", + "woman_student": "๐Ÿ‘ฉโ€๐ŸŽ“", + "woman_teacher": "๐Ÿ‘ฉโ€๐Ÿซ", + "woman_technologist": "๐Ÿ‘ฉโ€๐Ÿ’ป", + "woman_with_headscarf": "๐Ÿง•", + "woman_with_probing_cane": "๐Ÿ‘ฉโ€๐Ÿฆฏ", + "woman_with_turban": "๐Ÿ‘ณโ€โ™€๏ธ", + "woman_with_veil": "๐Ÿ‘ฐโ€โ™€๏ธ", + "womans_clothes": "๐Ÿ‘š", + "womans_hat": "๐Ÿ‘’", + "women_wrestling": "๐Ÿคผโ€โ™€๏ธ", + "womens": "๐Ÿšบ", + "wood": "๐Ÿชต", + "woozy_face": "๐Ÿฅด", + "world_map": "๐Ÿ—บ๏ธ", + "worm": "๐Ÿชฑ", + "worried": "๐Ÿ˜Ÿ", + "wrench": "๐Ÿ”ง", + "wrestling": "๐Ÿคผ", + "writing_hand": "โœ๏ธ", + "x": "โŒ", + "x_ray": "๐Ÿฉป", + "yarn": "๐Ÿงถ", + "yawning_face": "๐Ÿฅฑ", + "yellow_circle": "๐ŸŸก", + "yellow_heart": "๐Ÿ’›", + "yellow_square": "๐ŸŸจ", + "yemen": "๐Ÿ‡พ๐Ÿ‡ช", + "yen": "๐Ÿ’ด", + "yin_yang": "โ˜ฏ๏ธ", + "yo_yo": "๐Ÿช€", + "yum": "๐Ÿ˜‹", + "zambia": "๐Ÿ‡ฟ๐Ÿ‡ฒ", + "zany_face": "๐Ÿคช", + "zap": "โšก", + "zebra": "๐Ÿฆ“", + "zero": "0๏ธโƒฃ", + "zimbabwe": "๐Ÿ‡ฟ๐Ÿ‡ผ", + "zipper_mouth_face": "๐Ÿค", + "zombie": "๐ŸงŸ", + "zombie_man": "๐ŸงŸโ€โ™‚๏ธ", + "zombie_woman": "๐ŸงŸโ€โ™€๏ธ", + "zzz": "๐Ÿ’ค" +} diff --git a/resources/views/native/conversation.blade.php b/resources/views/native/conversation.blade.php index e16c1a0..480dd6c 100644 --- a/resources/views/native/conversation.blade.php +++ b/resources/views/native/conversation.blade.php @@ -139,40 +139,15 @@ class="w-full py-[4] text-theme-ink" @endif - {{-- React on a message: the reactions the web offers first, then the rest. Or an org's pinned messages. --}} - - @if ($sheet === \App\NativeComponents\Conversation::PinnedSheet) - @if ($pinned !== []) - @include('native.partials.pinned-sheet') - @endif - @elseif ($sheetMessage) - - - @foreach ($quickReactions as $emoji) - - - {{ $emoji }} - - - @endforeach - - {{-- EDGE's rows don't wrap on iOS, so the rest come in rows of eight. --}} - - @foreach (array_chunk($moreReactions, 8) as $row) - - @foreach ($row as $emoji) - - - {{ $emoji }} - - - @endforeach - - @endforeach - - + {{-- An org's pinned messages. --}} + + @if ($pinned !== []) + @include('native.partials.pinned-sheet') @endif @include('native.partials.mute-sheet') + + {{-- More reactions, from the bubble or anywhere else: the emoji keyboard on iOS, Jetpack's emoji picker on Android. --}} + diff --git a/resources/views/native/partials/message-actions.blade.php b/resources/views/native/partials/message-actions.blade.php index 368e37f..f302067 100644 --- a/resources/views/native/partials/message-actions.blade.php +++ b/resources/views/native/partials/message-actions.blade.php @@ -3,7 +3,8 @@ nearest it, then Reply, then React, with the web's icons. Every message keeps them, shrunk to nothing, and as the drag slides the message aside each grows in over its share of the way, nearest the bubble first, following the finger there and back. The slot never changes width: in an org it keeps room for Pin whether it shows or not, as the web's - does, so a pin arriving never rewraps a bubble. They only take taps once the message has settled open. + does, so a pin arriving never rewraps a bubble. They only take taps once the message has settled open. React opens + the web's quick reactions in a bubble pointing at it (see quick-reactions). A pinned message's pin shows all the time, nearest the bubble, so the others come in beyond it without moving it. For an org's maintainers it turns into Unpin, in the primary colour, while the others show; for everyone else it @@ -39,6 +40,10 @@ @endif @endforeach + {{-- React's bubble points at React: the last button of theirs, the first of yours. --}} + @if ($reactionsFor === $message->id && ! $reactsFromPinned) + @include('native.partials.quick-reactions', ['anchor' => 'absolute top-0 bottom-0 w-[30] '.($mine ? 'left-0' : 'right-0')]) + @endif @if ($isPinned && $mine) @include('native.partials.message-pin') diff --git a/resources/views/native/partials/pinned-message.blade.php b/resources/views/native/partials/pinned-message.blade.php index 70aff17..0cf9ddc 100644 --- a/resources/views/native/partials/pinned-message.blade.php +++ b/resources/views/native/partials/pinned-message.blade.php @@ -33,6 +33,10 @@ @endforeach + {{-- React's bubble points at React, the second button. --}} + @if ($reactionsFor === $message->id && $reactsFromPinned) + @include('native.partials.quick-reactions', ['anchor' => 'absolute top-0 bottom-0 left-[32] w-[32]']) + @endif @endif @if ($canPin) diff --git a/resources/views/native/partials/quick-reactions.blade.php b/resources/views/native/partials/quick-reactions.blade.php new file mode 100644 index 0000000..dd7a9b3 --- /dev/null +++ b/resources/views/native/partials/quick-reactions.blade.php @@ -0,0 +1,21 @@ +{{-- + React's bubble: the web's quick reactions for $message, yours tinted in your lane, and More for the phone's emoji + picker. It points at the React button it's laid over ($anchor places it there) and opens above it unless there's no + room. A tap outside closes it. +--}} + + + @foreach ($quickReactions as $emoji) + + + {{ $emoji }} + + + @endforeach + + + + + + + diff --git a/tests/Feature/Screens/ConversationTest.php b/tests/Feature/Screens/ConversationTest.php index cb4b810..0974949 100644 --- a/tests/Feature/Screens/ConversationTest.php +++ b/tests/Feature/Screens/ConversationTest.php @@ -311,17 +311,43 @@ function showsActions(TestableComponent $screen, string $key): bool expect(showsActions($screen, '1'))->toBeFalse(); }); -it('opens the reactions from React, without Reply in them', function () { +it("shows the web's quick reactions and More in a bubble pointing at React, and keeps the options up", function () { serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1])); $screen = finishRefreshes(Native::visit('/remotes/conversations/ada')) ->dragEnd('swipe-1', -200) ->tap('react-1') - ->assertSet('sheetOpen', true) + ->assertSet('reactionsFor', 1) + ->assertSet('actionsForId', 1) + ->assertSet('sheetOpen', false); + + $bubble = nodeByRef($screen, 'reactions-bubble'); + + expect($bubble['type'])->toBe('popover') + ->and($bubble['props']['visible'])->toBeTrue() + ->and(childRefs($screen, 'actions-1-buttons'))->toBe(['reply-1', 'react-1', 'reactions-bubble']) + ->and(childRefs($screen, 'quick-reactions'))->toBe(['quick-0', 'quick-1', 'quick-2', 'more-reactions']); + + // A tap outside it closes it, and leaves the options up. + $screen->dismissSheet('reactions-bubble') + ->assertSet('reactionsFor', null) + ->assertSet('actionsForId', 1); + + expect(rendersRef($screen, 'reactions-bubble'))->toBeFalse(); +}); + +it('reacts from the bubble, and puts the bubble and the options away', function () { + serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1])); + + $screen = finishRefreshes(Native::visit('/remotes/conversations/ada'))->dragEnd('swipe-1', -200)->tap('react-1'); + + finishRefreshes($screen->tap('quick-2')) + ->assertSet('reactionsFor', null) ->assertSet('actionsForId', null); - expect(childRefs($screen, 'quick-reactions'))->not->toBeEmpty() - ->and(findNode($screen->tree(), fn (array $node): bool => ($node['ref'] ?? null) === 'reply'))->toBeNull(); + expect(sentTo('POST', 'remotes/messages/1/reactions'))->toBe(['{"emoji":"โค๏ธ"}']) + ->and(rendersRef($screen, 'reactions-bubble'))->toBeFalse() + ->and(rendersRef($screen, 'chip-1-0'))->toBeTrue(); }); it('replies to a message from its Reply button, and raises the keyboard', function () { @@ -394,6 +420,82 @@ function showsActions(TestableComponent $screen, string $key): bool expect($screen->instance()->messages[0]->reactions)->toBe([['user_id' => RemotesViewerId, 'emoji' => 'โค๏ธ']]); }); +/** + * Whether the phone's emoji picker is up. + */ +function pickerShows(TestableComponent $screen): bool +{ + return nodeByRef($screen, 'emoji-picker')['props']['visible'] ?? false; +} + +it("opens the phone's emoji picker from More in the bubble, and reacts with the emoji picked", function () { + serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1])); + + $screen = finishRefreshes(Native::visit('/remotes/conversations/ada'))->dragEnd('swipe-1', -200)->tap('react-1'); + + expect(pickerShows($screen))->toBeFalse(); + + $screen->tap('more-reactions') + ->assertSet('reactionsFor', null) + ->assertSet('emojiPickerFor', 1); + + expect(pickerShows($screen))->toBeTrue() + ->and(rendersRef($screen, 'reactions-bubble'))->toBeFalse(); + + finishRefreshes($screen->select('emoji-picker', '๐ŸŽ‰'))->assertSee('๐ŸŽ‰'); + + expect(sentTo('POST', 'remotes/messages/1/reactions'))->toBe(['{"emoji":"๐ŸŽ‰"}']) + ->and(pickerShows($screen))->toBeFalse(); +}); + +it('takes back a reaction picked again, as the quick reactions do', function () { + serveRemoteThread(threadJson([remoteMessageJson(['id' => 1, 'reactions' => [['user_id' => RemotesViewerId, 'emoji' => '๐ŸŽ‰']]])], ['read_through_id' => 1])); + + finishRefreshes(finishRefreshes(Native::visit('/remotes/conversations/ada'))->call('openEmojiPicker', 1)->select('emoji-picker', '๐ŸŽ‰')); + + expect(sentTo('DELETE', 'remotes/messages/1/reactions'))->toBe(['{"emoji":"๐ŸŽ‰"}']); +}); + +it('reacts with the emoji as devnull has it when the picker adds a skin tone or leaves out the variation selector', function (string $picked, string $sent) { + serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1])); + + finishRefreshes(finishRefreshes(Native::visit('/remotes/conversations/ada'))->call('openEmojiPicker', 1)->select('emoji-picker', $picked)); + + expect(array_map(fn (string $body): string => json_decode($body, true)['emoji'], sentTo('POST', 'remotes/messages/1/reactions')))->toBe([$sent]); +})->with([ + 'a skin tone' => ['๐Ÿ‘๐Ÿฝ', '๐Ÿ‘'], + 'a skin tone on an emoji that needs the variation selector' => ['โœŒ๐Ÿฝ', 'โœŒ๏ธ'], + 'a skin tone inside a sequence' => ['๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿ’ป', '๐Ÿ‘ฉโ€๐Ÿ’ป'], + 'no variation selector' => ['โค', 'โค๏ธ'], + 'as it is' => ['๐Ÿซ ', '๐Ÿซ '], +]); + +it("doesn't send an emoji devnull doesn't know, says so, and keeps the picker up for another", function (string $picked) { + serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1])); + + $screen = finishRefreshes(finishRefreshes(Native::visit('/remotes/conversations/ada'))->call('openEmojiPicker', 1)->select('emoji-picker', $picked)); + + expect(sentTo('POST', 'remotes/messages/1/reactions'))->toBe([]) + ->and(array_column(array_column($this->bridge->callsTo('ToastKit.Show'), 'params'), 'message'))->toBe(["devnull doesn't take {$picked} as a reaction yet. Pick another."]) + ->and(pickerShows($screen))->toBeTrue(); + + finishRefreshes($screen->select('emoji-picker', '๐Ÿš€')); + + expect(sentTo('POST', 'remotes/messages/1/reactions'))->toBe(['{"emoji":"๐Ÿš€"}']); +})->with([ + 'newer than devnull\'s set' => ['๐Ÿฆโ€๐Ÿ”ฅ'], + 'two skin tones in one' => ['๐Ÿซฑ๐Ÿปโ€๐Ÿซฒ๐Ÿฟ'], +]); + +it('sends nothing when the picker is closed without a pick', function () { + serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1])); + + $screen = finishRefreshes(Native::visit('/remotes/conversations/ada'))->call('openEmojiPicker', 1)->dismissSheet('emoji-picker'); + + expect(pickerShows($screen))->toBeFalse() + ->and(sentTo('POST', 'remotes/messages/1/reactions'))->toBe([]); +}); + it("says why a message can't go, in the web's words, and merges or drops a request", function (string $access, string $words) { app(Account::class)->rememberRemotes(new Remotes(requests: true)); serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1, 'access' => $access])); @@ -861,7 +963,6 @@ function serveOrgThread(array $thread, ?array $poll = null, ?Closure $pins = nul $screen->tap('pinned-bar') ->assertSet('sheetOpen', true) - ->assertSet('sheet', Conversation::PinnedSheet) ->assertSee('Pinned messages') ->assertSee('Yesterday ยท 10:02'); @@ -876,10 +977,15 @@ function serveOrgThread(array $thread, ?array $poll = null, ?Closure $pins = nul $screen = finishRefreshes(Native::visit('/remotes/orgs/org-1'))->tap('pinned-bar'); - // React shows the reactions in the sheet, and reacting goes back to the pinned messages. - $screen->tap('pin-1-react')->assertSet('sheet', Conversation::ReactionsSheet); + // React shows the bubble over the sheet, and reacting leaves the pinned messages up. + $screen->tap('pin-1-react') + ->assertSet('reactionsFor', 1) + ->assertSet('reactsFromPinned', true); + + expect(childRefs($screen, 'pin-1-actions'))->toContain('reactions-bubble'); + finishRefreshes($screen->tap('quick-0')) - ->assertSet('sheet', Conversation::PinnedSheet) + ->assertSet('reactionsFor', null) ->assertSet('sheetOpen', true); expect(sentTo('POST', 'remotes/messages/1/reactions'))->toHaveCount(1) @@ -896,6 +1002,26 @@ function serveOrgThread(array $thread, ?array $poll = null, ?Closure $pins = nul expect(json_decode(sentTo('POST', 'remotes/orgs/org-1/messages')[0], true)['reply_to_id'])->toBe(1); }); +it('goes back to the pinned messages from the emoji picker, picked or not', function (?string $picked) { + serveOrgThread(orgThreadJson([orgMessageJson(['id' => 5, 'body' => 'hello org'])], ['pinned' => [ + orgMessageJson(['id' => 1, 'body' => 'standup in 10?']), + ]])); + + $screen = finishRefreshes(Native::visit('/remotes/orgs/org-1'))->tap('pinned-bar')->tap('pin-1-react')->tap('more-reactions') + // iOS tells the sheet it closed once it has made way for the picker. + ->dismissSheet('message-sheet') + ->assertSet('sheetOpen', false); + + finishRefreshes($picked === null ? $screen->dismissSheet('emoji-picker') : $screen->select('emoji-picker', $picked)) + ->assertSet('sheetOpen', true) + ->assertSet('emojiPickerFor', null); + + expect(sentTo('POST', 'remotes/messages/1/reactions'))->toHaveCount($picked === null ? 0 : 1); +})->with([ + 'picked' => ['๐ŸŽ‰'], + 'closed' => [null], +]); + it('shows three lines of a long pinned message, and the rest with Show more', function () { $long = implode("\n", ['release checklist for today:', '1. tag 2.4', '2. post the changelog', '3. ping the mobile folks']); serveOrgThread(orgThreadJson([orgMessageJson(['id' => 2, 'body' => 'short'])], ['pinned' => [