From a5865153a24f070286326311a5c786f616302f26 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Tue, 2 Jul 2019 19:15:35 +0200
Subject: [PATCH 1/3] Add Emoji::Character#skin_tones?

---
 lib/emoji.rb           | 1 +
 lib/emoji/character.rb | 6 ++++++
 test/emoji_test.rb     | 8 ++++++++
 3 files changed, 15 insertions(+)

diff --git a/lib/emoji.rb b/lib/emoji.rb
index 7d3d8017..4c76bf42 100644
--- a/lib/emoji.rb
+++ b/lib/emoji.rb
@@ -118,6 +118,7 @@ def parse_data_file
           emoji.description = dedup.call(raw_emoji[:description])
           emoji.unicode_version = dedup.call(raw_emoji[:unicode_version])
           emoji.ios_version = dedup.call(raw_emoji[:ios_version])
+          emoji.skin_tones = raw_emoji.fetch(:skin_tones, false)
         end
       end
     end
diff --git a/lib/emoji/character.rb b/lib/emoji/character.rb
index 42079392..ab87b948 100644
--- a/lib/emoji/character.rb
+++ b/lib/emoji/character.rb
@@ -11,6 +11,11 @@ def self.hex_inspect(str)
     # True if the emoji is not a standard Emoji character.
     def custom?() !raw end
 
+    # True if the emoji supports Fitzpatrick scale skin tone modifiers
+    def skin_tones?() @skin_tones end
+
+    attr_writer :skin_tones
+
     # A list of names uniquely referring to this emoji.
     attr_reader :aliases
 
@@ -54,6 +59,7 @@ def initialize(name)
       @aliases = Array(name)
       @unicode_aliases = []
       @tags = []
+      @skin_tones = false
     end
 
     def inspect
diff --git a/test/emoji_test.rb b/test/emoji_test.rb
index 06381739..cf1edb97 100644
--- a/test/emoji_test.rb
+++ b/test/emoji_test.rb
@@ -153,6 +153,14 @@ class EmojiTest < TestCase
     assert_equal '8.3', emoji.ios_version
   end
 
+  test "skin tones" do
+    smiley = Emoji.find_by_alias("smiley")
+    assert_equal false, smiley.skin_tones?
+
+    wave = Emoji.find_by_alias("wave")
+    assert_equal true, wave.skin_tones?
+  end
+
   test "no custom emojis" do
     custom = Emoji.all.select(&:custom?)
     assert 0, custom.size

From ea4d73899aa7cb88cfa9967712a4e6b1a58412a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <git@mislav.net>
Date: Tue, 15 Nov 2022 20:40:52 +0100
Subject: [PATCH 2/3] People holding hands emoji doesn't seem to support skin
 tones rendering (macOS)

---
 db/emoji.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/db/emoji.json b/db/emoji.json
index 6e425ca6..5cd0d266 100644
--- a/db/emoji.json
+++ b/db/emoji.json
@@ -6416,7 +6416,7 @@
     ]
   , "unicode_version": "12.0"
   , "ios_version": "13.0"
-  , "skin_tones": true
+  , "skin_tones": false
   }
 , {
     "emoji": "👭"

From 744495f776117b073c4950eba2afd95c4884058d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <git@mislav.net>
Date: Tue, 15 Nov 2022 20:41:33 +0100
Subject: [PATCH 3/3] Expand skin tones support

- Lookup emoji by unicode that contains a skin tone modifier
- Generate skin tone variants from an emoji
---
 lib/emoji.rb           |  5 +++--
 lib/emoji/character.rb | 25 +++++++++++++++++++++++++
 test/emoji_test.rb     | 13 +++++++++++++
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/lib/emoji.rb b/lib/emoji.rb
index 4c76bf42..d810a25e 100644
--- a/lib/emoji.rb
+++ b/lib/emoji.rb
@@ -51,11 +51,12 @@ def find_by_alias(name)
 
   # Public: Find an emoji by its unicode character. Return nil if missing.
   def find_by_unicode(unicode)
-    unicodes_index[unicode]
+    unicodes_index[unicode] || unicodes_index[unicode.sub(SKIN_TONE_RE, "")]
   end
 
   private
     VARIATION_SELECTOR_16 = "\u{fe0f}".freeze
+    SKIN_TONE_RE = /[\u{1F3FB}-\u{1F3FF}]/
 
     # Characters which must have VARIATION_SELECTOR_16 to render as color emoji:
     TEXT_GLYPHS = [
@@ -70,7 +71,7 @@ def find_by_unicode(unicode)
       "\u{3030}",  # wavy dash
     ].freeze
 
-    private_constant :VARIATION_SELECTOR_16, :TEXT_GLYPHS
+    private_constant :VARIATION_SELECTOR_16, :TEXT_GLYPHS, :SKIN_TONE_RE
 
     def parse_data_file
       data = File.open(data_file, 'r:UTF-8') do |file|
diff --git a/lib/emoji/character.rb b/lib/emoji/character.rb
index ab87b948..25504aa9 100644
--- a/lib/emoji/character.rb
+++ b/lib/emoji/character.rb
@@ -43,6 +43,21 @@ def add_alias(name)
     # Raw Unicode string for an emoji. Nil if emoji is non-standard.
     def raw() unicode_aliases.first end
 
+    # Raw Unicode strings for each skin tone variant of this emoji.
+    def raw_skin_tone_variants
+      return [] if custom? || !skin_tones?
+      raw_normalized = raw.sub("\u{fe0f}", "") # strip VARIATION_SELECTOR_16
+      idx = raw_normalized.index("\u{200d}") # detect zero-width joiner
+      SKIN_TONES.map do |modifier|
+        if idx
+          # insert modifier before zero-width joiner
+          raw_normalized[...idx] + modifier + raw_normalized[idx..]
+        else
+          raw_normalized + modifier
+        end
+      end
+    end
+
     def add_unicode_alias(str)
       unicode_aliases << str
     end
@@ -82,6 +97,16 @@ def image_filename
     end
 
     private
+    
+    SKIN_TONES = [
+      "\u{1F3FB}", # light skin tone
+      "\u{1F3FC}", # medium-light skin tone
+      "\u{1F3FD}", # medium skin tone
+      "\u{1F3FE}", # medium-dark skin tone
+      "\u{1F3FF}", # dark skin tone
+    ]
+
+    private_constant :SKIN_TONES
 
     def default_image_filename
       if custom?
diff --git a/test/emoji_test.rb b/test/emoji_test.rb
index a6a006fc..08aba3ce 100644
--- a/test/emoji_test.rb
+++ b/test/emoji_test.rb
@@ -155,9 +155,22 @@ class EmojiTest < TestCase
   test "skin tones" do
     smiley = Emoji.find_by_alias("smiley")
     assert_equal false, smiley.skin_tones?
+    assert_equal [], smiley.raw_skin_tone_variants
 
     wave = Emoji.find_by_alias("wave")
     assert_equal true, wave.skin_tones?
+
+    wave = Emoji.find_by_unicode("\u{1f44b}\u{1f3ff}") # wave + dark skin tone
+    assert_equal "wave", wave.name
+
+    woman_with_beard = Emoji.find_by_unicode("\u{1f9d4}\u{200d}\u{2640}\u{fe0f}")
+    assert_equal [
+      "1f9d4-1f3fb-200d-2640",
+      "1f9d4-1f3fc-200d-2640",
+      "1f9d4-1f3fd-200d-2640",
+      "1f9d4-1f3fe-200d-2640",
+      "1f9d4-1f3ff-200d-2640",
+    ], woman_with_beard.raw_skin_tone_variants.map { |u| Emoji::Character.hex_inspect(u) }
   end
 
   test "no custom emojis" do
