From 948bd00340d12fdd68aec06fee8eccd21d35b7e9 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Thu, 16 Jan 2020 09:54:26 +0100 Subject: [PATCH] FEATURE: line with only 1 to 3 emojis will now display as large emojis --- .../engines/discourse-markdown/emoji.js.es6 | 66 ++++++++++++++++++- app/assets/stylesheets/common/base/emoji.scss | 6 ++ spec/components/cooked_post_processor_spec.rb | 10 +-- spec/components/pretty_text_spec.rb | 46 ++++++++++++- test/javascripts/lib/pretty-text-test.js.es6 | 8 +-- 5 files changed, 124 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 index ba2c971541b..9f2c2dfacd1 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 @@ -231,9 +231,68 @@ function applyEmoji( result.push(text); } + // we check for a result <= 5 because we support maximum 3 large emojis + // EMOJI SPACE EMOJI SPACE EMOJI => 5 tokens + if (result && result.length > 0 && result.length <= 5) { + // we ensure line starts and ends with an emoji + // and has no more than 3 emojis + if ( + result[0].type === "emoji" && + result[result.length - 1].type === "emoji" && + result.filter(r => r.type === "emoji").length <= 3 + ) { + let allEmojiLine = true; + let index = 0; + + const checkNextToken = t => { + if (!t) { + return; + } + + if (!["emoji", "text"].includes(t.type)) { + allEmojiLine = false; + } + + // a text token should always have an emoji before + // and be a space + if ( + t.type === "text" && + ((result[index - 1] && result[index - 1].type !== "emoji") || + t.content !== " ") + ) { + allEmojiLine = false; + } + + // exit as soon as possible + if (allEmojiLine) { + index += 1; + checkNextToken(result[index]); + } + }; + + checkNextToken(result[index]); + + if (allEmojiLine) { + result.forEach(r => { + if (r.type === "emoji") { + applyAllEmojiClass(r); + } + }); + } + } + } + return result; } +function applyAllEmojiClass(token) { + token.attrs.forEach(attr => { + if (attr[0] === "class") { + attr[1] = `${attr[1]} only-emoji`; + } + }); +} + export function setup(helper) { helper.registerOptions((opts, siteSettings, state) => { opts.features.emoji = !state.disableEmojis && !!siteSettings.enable_emoji; @@ -257,5 +316,10 @@ export function setup(helper) { ); }); - helper.whiteList(["img[class=emoji]", "img[class=emoji emoji-custom]"]); + helper.whiteList([ + "img[class=emoji]", + "img[class=emoji emoji-custom]", + "img[class=emoji emoji-custom only-emoji]", + "img[class=emoji only-emoji]" + ]); } diff --git a/app/assets/stylesheets/common/base/emoji.scss b/app/assets/stylesheets/common/base/emoji.scss index 0c59b17842d..09fde6478fe 100644 --- a/app/assets/stylesheets/common/base/emoji.scss +++ b/app/assets/stylesheets/common/base/emoji.scss @@ -4,6 +4,12 @@ img.emoji { vertical-align: middle; } +img.emoji.only-emoji { + width: 32px; + height: 32px; + margin: 0.5em 0; +} + small img.emoji, sub img.emoji, sup img.emoji { diff --git a/spec/components/cooked_post_processor_spec.rb b/spec/components/cooked_post_processor_spec.rb index 99fda0ad24f..f087616a936 100644 --- a/spec/components/cooked_post_processor_spec.rb +++ b/spec/components/cooked_post_processor_spec.rb @@ -1143,7 +1143,7 @@ describe CookedPostProcessor do Google

text.txt (20 Bytes)
- :smile:

+ :smile:

HTML end @@ -1158,7 +1158,7 @@ describe CookedPostProcessor do Google

text.txt (20 Bytes)
- :smile:

+ :smile:

HTML end @@ -1171,7 +1171,7 @@ describe CookedPostProcessor do Google

text.txt (20 Bytes)
- :smile:

+ :smile:

HTML end @@ -1185,7 +1185,7 @@ describe CookedPostProcessor do Google

text.txt (20 Bytes)
- :smile:

+ :smile:

HTML end @@ -1199,7 +1199,7 @@ describe CookedPostProcessor do Google

text.txt (20 Bytes)
- :smile:

+ :smile:

HTML end diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index c87a449964e..4beb8cfc473 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -109,6 +109,48 @@ describe PrettyText do expect(cook(md)).to eq(html.strip) end + + it "adds an only-emoji class when a line has only one emoji" do + md = <<~MD + foo šŸ˜€ + foo šŸ˜€ bar + :smile_cat: + :smile_cat: :smile_cat: + :smile_cat: :smile_cat: :smile_cat: :smile_cat: + baz? :smile_cat: + šŸ˜€ + šŸ˜‰ foo + šŸ˜‰ šŸ˜‰ + šŸ˜‰ šŸ˜‰ + šŸ˜‰ šŸ˜‰ šŸ˜‰ + šŸ˜‰šŸ˜‰šŸ˜‰ + šŸ˜‰ šŸ˜‰ šŸ˜‰ + šŸ˜‰dšŸ˜‰ šŸ˜‰ + šŸ˜‰ šŸ˜‰ šŸ˜‰d + šŸ˜‰šŸ˜‰šŸ˜‰šŸ˜‰ + MD + + html = <<~HTML +

foo :grinning:
+ foo :grinning: bar
+ :smile_cat:
+ :smile_cat: :smile_cat:
+ :smile_cat: :smile_cat: :smile_cat: :smile_cat:
+ baz? :smile_cat:
+ :grinning:
+ :wink: foo
+ :wink: :wink:
+ :wink: :wink:
+ :wink: :wink: :wink:
+ :wink::wink::wink:
+ :wink: :wink: :wink:
+ :wink:d​:wink: :wink:
+ :wink: :wink: :wink:d
+ :wink::wink::wink::wink:

+ HTML + + expect(cook(md)).to eq(html.strip) + end end it "do off topic quoting of posts from secure categories" do @@ -1064,7 +1106,7 @@ describe PrettyText do it "can handle emoji by name" do expected = <:smile::sunny:

+

:smile::sunny:

HTML expect(PrettyText.cook(":smile::sunny:")).to eq(expected.strip) end @@ -1076,7 +1118,7 @@ HTML end it "can handle emoji by translation" do - expected = "

\":wink:\"

" + expected = "

\":wink:\"

" expect(PrettyText.cook(";)")).to eq(expected) end diff --git a/test/javascripts/lib/pretty-text-test.js.es6 b/test/javascripts/lib/pretty-text-test.js.es6 index d43310392eb..ea8918d92f3 100644 --- a/test/javascripts/lib/pretty-text-test.js.es6 +++ b/test/javascripts/lib/pretty-text-test.js.es6 @@ -1333,15 +1333,15 @@ QUnit.test("enable/disable features", assert => { QUnit.test("emoji", assert => { assert.cooked( ":smile:", - `

:smile:

` + `

:smile:

` ); assert.cooked( ":(", - `

:frowning:

` + `

:frowning:

` ); assert.cooked( "8-)", - `

:sunglasses:

` + `

:sunglasses:

` ); }); @@ -1363,7 +1363,7 @@ QUnit.test("emoji - emojiSet", assert => { assert.cookedOptions( ":smile:", { siteSettings: { emoji_set: "twitter" } }, - `

:smile:

` + `

:smile:

` ); });