Skip to content

Commit 8efd99d

Browse files
authored
FEATURE: Improve emoji diversity rendering (#32909)
This change polishes the emoji diversity rendering behaviour in the emoji pickers. When a user changes their diversity setting, that setting will now be applied to favourites, chat default reactions, and in the emoji picker section selectors. If an emoji was previously stored with a skin tone in favourites or default reactions, the stored skin tone won't be overridden.
1 parent 012b48d commit 8efd99d

File tree

8 files changed

+97
-5
lines changed

8 files changed

+97
-5
lines changed

app/assets/javascripts/discourse/app/components/emoji-picker/content.gjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,10 @@ export default class EmojiPicker extends Component {
503503
width="18"
504504
height="18"
505505
class="emoji"
506-
src={{get emojis "0.url"}}
506+
src={{tonableEmojiUrl
507+
(get emojis "0")
508+
this.emojiStore.diversity
509+
}}
507510
/>
508511
{{/if}}
509512
</DButton>

app/assets/javascripts/discourse/app/services/emoji-store.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { tracked } from "@glimmer/tracking";
22
import Service, { service } from "@ember/service";
33
import { TrackedArray, TrackedObject } from "@ember-compat/tracked-built-ins";
4+
import { isSkinTonableEmoji } from "pretty-text/emoji";
45
import KeyValueStore from "discourse/lib/key-value-store";
56

67
export const SKIN_TONE_STORE_KEY = "emojiSelectedDiversity";
@@ -38,9 +39,22 @@ export default class EmojiStore extends Service {
3839
}
3940

4041
favoritesForContext(context) {
41-
return this.#sortEmojisByFrequency(
42+
const data = this.#sortEmojisByFrequency(
4243
this.#recentEmojisForContext(context)
43-
).slice(0, MAX_DISPLAYED_EMOJIS);
44+
)
45+
.slice(0, MAX_DISPLAYED_EMOJIS)
46+
.map((emoji) => {
47+
if (
48+
this.diversity === DEFAULT_DIVERSITY ||
49+
!isSkinTonableEmoji(emoji)
50+
) {
51+
return emoji;
52+
}
53+
54+
return `${emoji}:t${this.diversity}`;
55+
});
56+
57+
return data;
4458
}
4559

4660
reset() {

app/assets/javascripts/discourse/tests/integration/components/emoji-picker-test.gjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ module("Integration | Component | emoji-picker-content", function (hooks) {
5151
assert
5252
.dom(".emoji-picker__diversity-trigger img[title='clap:t6']")
5353
.exists("it changes the current scale to t6");
54+
assert
55+
.dom(
56+
`.emoji-picker__section-btn img[src="/images/emoji/twitter/raised_hands/6.png?v=${v}"]`
57+
)
58+
.exists("it applies the tone to the section selector");
5459
});
5560

5661
test("When requesting section", async function (assert) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { setupTest } from "ember-qunit";
2+
import { isSkinTonableEmoji } from "pretty-text/emoji";
3+
import { module, test } from "qunit";
4+
5+
module("Unit | Pretty Text | Emoji", function (hooks) {
6+
setupTest(hooks);
7+
8+
test("isSkinTonableEmoji", async function (assert) {
9+
assert.false(isSkinTonableEmoji(":smile:"));
10+
assert.false(isSkinTonableEmoji("smile"));
11+
assert.false(isSkinTonableEmoji("smile:t1"));
12+
assert.true(isSkinTonableEmoji(":woman_artist:"));
13+
assert.false(isSkinTonableEmoji(":woman_artist:t1:"));
14+
});
15+
});

app/assets/javascripts/discourse/tests/unit/services/emoji-store-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ module("Unit | Service | emoji-store", function (hooks) {
9191
assert.deepEqual(storedDiversity, 2, "it persists the diversity value");
9292
});
9393

94+
test("skin tones are added to favourites after diversity is set", function (assert) {
95+
this.emojiStore.trackEmojiForContext("-1:t3", "topic");
96+
this.emojiStore.trackEmojiForContext("+1", "topic");
97+
this.emojiStore.diversity = 2;
98+
99+
assert.deepEqual(this.emojiStore.favoritesForContext("topic"), [
100+
"+1:t2",
101+
"-1:t3",
102+
]);
103+
});
104+
94105
test("sort emojis by frequency", function (assert) {
95106
this.emojiStore.trackEmojiForContext("grinning", "topic");
96107
this.emojiStore.trackEmojiForContext("cat", "topic");

app/assets/javascripts/pretty-text/addon/emoji.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,24 @@ export function emojiSearch(term, options) {
242242
}
243243
}
244244

245+
/**
246+
* Returns true if the given emoji term is skin tonable.
247+
*
248+
* A skin tonable emoji is one that can be suffixed with a tone modifier (e.g. :t1:, :t2:, etc.)
249+
* to change the skin tone of the emoji.
250+
*
251+
* If the emoji already has a tone modifier, it is not considered skin tonable.
252+
*
253+
* @param {string} term The emoji term to check, with or without colons or tone modifiers.
254+
* @returns {boolean} True if the emoji is skin tonable, false otherwise.
255+
*/
245256
export function isSkinTonableEmoji(term) {
257+
// Check if the emoji term already has a tone modifier
258+
if (/:t[1-6]:?$/.test(term)) {
259+
return false;
260+
}
261+
262+
// Extract the base emoji from any wrapping colons or whitespace
246263
const match = term.split(":").filter(Boolean)[0];
247264
if (match) {
248265
return tonableEmojis.includes(match);

plugins/chat/assets/javascripts/discourse/lib/chat-message-interactor.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { tracked } from "@glimmer/tracking";
22
import { action } from "@ember/object";
33
import { getOwner, setOwner } from "@ember/owner";
44
import { service } from "@ember/service";
5+
import { isSkinTonableEmoji } from "pretty-text/emoji";
56
import EmojiPickerDetached from "discourse/components/emoji-picker/detached";
67
import BookmarkModal from "discourse/components/modal/bookmark";
78
import FlagModal from "discourse/components/modal/flag";
@@ -11,6 +12,7 @@ import { bind } from "discourse/lib/decorators";
1112
import getURL from "discourse/lib/get-url";
1213
import { clipboardCopy } from "discourse/lib/utilities";
1314
import Bookmark from "discourse/models/bookmark";
15+
import { DEFAULT_DIVERSITY } from "discourse/services/emoji-store";
1416
import { i18n } from "discourse-i18n";
1517
import { MESSAGE_CONTEXT_THREAD } from "discourse/plugins/chat/discourse/components/chat-message";
1618
import ChatMessageFlag from "discourse/plugins/chat/discourse/lib/chat-message-flag";
@@ -70,8 +72,18 @@ export default class ChatemojiReactions {
7072

7173
const frequentReactions = this.emojiStore.favoritesForContext("chat");
7274

73-
const defaultReactions =
74-
this.siteSettings.default_emoji_reactions.split("|");
75+
const defaultReactions = this.siteSettings.default_emoji_reactions
76+
.split("|")
77+
.map((emoji) => {
78+
if (
79+
this.emojiStore.diversity !== DEFAULT_DIVERSITY &&
80+
isSkinTonableEmoji(emoji)
81+
) {
82+
return `${emoji}:t${this.emojiStore.diversity}`;
83+
}
84+
85+
return emoji;
86+
});
7587

7688
const allReactionsInOrder = userQuickReactionsCustom
7789
.concat(frequentReactions)

plugins/chat/test/javascripts/unit/lib/chat-message-interactor-test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ module("Discourse Chat | Unit | chat-message-interactor", function (hooks) {
4545
);
4646
});
4747

48+
test("emojiReactions with diversity set applies to site defaults", function (assert) {
49+
updateCurrentUser({
50+
user_option: {
51+
chat_quick_reaction_type: "frequent",
52+
},
53+
});
54+
55+
this.emojiStore.diversity = 2;
56+
57+
assert.deepEqual(
58+
this.messageInteractor.emojiReactions.map((r) => r.emoji),
59+
["+1:t2", "heart", "tada"]
60+
);
61+
});
62+
4863
test("emojiReactions with top 3 frequent", function (assert) {
4964
this.emojiStore.trackEmojiForContext("eyes", "chat");
5065
this.emojiStore.trackEmojiForContext("camera", "chat");

0 commit comments

Comments
 (0)