From 47211fb32cb18809912db6c939fdd3a17d3d4569 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sat, 9 Mar 2019 02:15:35 +0200
Subject: [PATCH 1/3] emoji adder

---
 .../entity_normalizer.service.js                 | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index d20ce77f..633bd3dc 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -40,10 +40,10 @@ export const parseUser = (data) => {
     }
 
     output.name = null // missing
-    output.name_html = data.display_name
+    output.name_html = addEmojis(data.display_name, data.emojis)
 
     output.description = null // missing
-    output.description_html = data.note
+    output.description_html = addEmojis(data.note, data.emojis)
 
     // Utilize avatar_static for gif avatars?
     output.profile_image_url = data.avatar
@@ -142,6 +142,14 @@ const parseAttachment = (data) => {
 
   return output
 }
+const addEmojis = (string, emojis) => {
+  return emojis.reduce((acc, emoji) => {
+    return acc.replace(
+      new RegExp(`:${emoji.shortcode}:`, 'g'),
+      `<img src='${emoji.url}' alt='${emoji.shortcode}' class='emoji' />`
+    )
+  }, string)
+}
 
 export const parseStatus = (data) => {
   const output = {}
@@ -157,7 +165,7 @@ export const parseStatus = (data) => {
     output.type = data.reblog ? 'retweet' : 'status'
     output.nsfw = data.sensitive
 
-    output.statusnet_html = data.content
+    output.statusnet_html = addEmojis(data.content, data.emojis)
 
     // Not exactly the same but works?
     output.text = data.content
@@ -176,7 +184,7 @@ export const parseStatus = (data) => {
     }
 
     output.summary = data.spoiler_text
-    output.summary_html = data.spoiler_text
+    output.summary_html = addEmojis(data.spoiler_text, data.emojis)
     output.external_url = data.url
 
     // FIXME missing!!

From 4efcda1b4137fa14659a586d4d8559dcdd0f479b Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 11 Mar 2019 22:41:08 +0200
Subject: [PATCH 2/3] Added some tests

---
 .../entity_normalizer.service.js              |  2 +-
 .../entity_normalizer.spec.js                 | 75 ++++++++++++++++++-
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 633bd3dc..b7e5711e 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -142,7 +142,7 @@ const parseAttachment = (data) => {
 
   return output
 }
-const addEmojis = (string, emojis) => {
+export const addEmojis = (string, emojis) => {
   return emojis.reduce((acc, emoji) => {
     return acc.replace(
       new RegExp(`:${emoji.shortcode}:`, 'g'),
diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js
index 6245361c..2b0b0d6d 100644
--- a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js
+++ b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js
@@ -1,4 +1,4 @@
-import { parseStatus, parseUser, parseNotification } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
+import { parseStatus, parseUser, parseNotification, addEmojis } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
 import mastoapidata from '../../../../fixtures/mastoapi.json'
 import qvitterapidata from '../../../../fixtures/statuses.json'
 
@@ -143,6 +143,23 @@ const makeMockNotificationQvitter = (overrides = {}) => {
   }, overrides)
 }
 
+const makeMockEmojiMasto = (overrides = [{}]) => {
+  return [
+    Object.assign({
+      shortcode: 'image',
+      static_url: 'https://example.com/image.png',
+      url: 'https://example.com/image.png',
+      visible_in_picker: false
+    }, overrides[0]),
+    Object.assign({
+      shortcode: 'thinking',
+      static_url: 'https://example.com/think.png',
+      url: 'https://example.com/think.png',
+      visible_in_picker: false
+    }, overrides[1])
+  ]
+}
+
 parseNotification
 parseUser
 parseStatus
@@ -218,6 +235,22 @@ describe('API Entities normalizer', () => {
         expect(parsedRepeat).to.have.property('retweeted_status')
         expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef')
       })
+
+      it('adds emojis to post content', () => {
+        const post = makeMockStatusMasto({ emojis: makeMockEmojiMasto(), content: 'Makes you think :thinking:' })
+
+        const parsedPost = parseStatus(post)
+
+        expect(parsedPost).to.have.property('statusnet_html').that.contains('<img')
+      })
+
+      it('adds emojis to subject line', () => {
+        const post = makeMockStatusMasto({ emojis: makeMockEmojiMasto(), spoiler_text: 'CW: 300 IQ :thinking:' })
+
+        const parsedPost = parseStatus(post)
+
+        expect(parsedPost).to.have.property('summary_html').that.contains('<img')
+      })
     })
   })
 
@@ -230,6 +263,22 @@ describe('API Entities normalizer', () => {
       expect(parseUser(local)).to.have.property('is_local', true)
       expect(parseUser(remote)).to.have.property('is_local', false)
     })
+
+    it('adds emojis to user name', () => {
+      const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), display_name: 'The :thinking: thinker' })
+
+      const parsedUser = parseUser(user)
+
+      expect(parsedUser).to.have.property('name_html').that.contains('<img')
+    })
+
+    it('adds emojis to user bio', () => {
+      const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), note: 'Hello i like to :thinking: a lot' })
+
+      const parsedUser = parseUser(user)
+
+      expect(parsedUser).to.have.property('description_html').that.contains('<img')
+    })
   })
 
   // We currently use QvitterAPI notifications only, and especially due to MastoAPI lacking is_seen, support for MastoAPI
@@ -267,4 +316,28 @@ describe('API Entities normalizer', () => {
       expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo')
     })
   })
+
+  describe('MastoAPI emoji adder', () => {
+    const emojis = makeMockEmojiMasto()
+    const imageHtml = '<img src="https://example.com/image.png" alt="image" class="emoji" />'
+          .replace(/"/g, '\'')
+    const thinkHtml = '<img src="https://example.com/think.png" alt="thinking" class="emoji" />'
+          .replace(/"/g, '\'')
+
+    it('correctly replaces shortcodes in supplied string', () => {
+      const result = addEmojis('This post has :image: emoji and :thinking: emoji', emojis)
+      expect(result).to.include(thinkHtml)
+      expect(result).to.include(imageHtml)
+    })
+
+    it('handles consecutive emojis correctly', () => {
+      const result = addEmojis('Lelel emoji spam :thinking::thinking::thinking::thinking:', emojis)
+      expect(result).to.include(thinkHtml + thinkHtml + thinkHtml + thinkHtml)
+    })
+
+    it('Doesn\'t replace nonexistent emojis', () => {
+      const result = addEmojis('Admin add the :tenshi: emoji', emojis)
+      expect(result).to.equal('Admin add the :tenshi: emoji')
+    })
+  })
 })

From 4cdfd5fb8678fae7dbba8b00b2a468d3ce385e08 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 17 Mar 2019 14:41:05 +0200
Subject: [PATCH 3/3] post-merge fixes

---
 src/services/entity_normalizer/entity_normalizer.service.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 02130dcf..e831963a 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -40,10 +40,10 @@ export const parseUser = (data) => {
     }
 
     // output.name = ??? missing
-    output.name_html = data.display_name
+    output.name_html = addEmojis(data.display_name, data.emojis)
 
     // output.description = ??? missing
-    output.description_html = data.note
+    output.description_html = addEmojis(data.note, data.emojis)
 
     // Utilize avatar_static for gif avatars?
     output.profile_image_url = data.avatar