FEATURE: One-click chat reaction settings (#32150)

Adds a one-click chat reactions setting to the chat preferences page
where members can determine what one-click reactions are shown in chat.

- Frequent: This will be the default setting. (Automatically set based
on most used chat reactions)
- Custom: Members can choose up to three reactions they want to see in
their one-click chat/DM reactions menu. Defaults are `❤️`, `👍` ,
and `😄`.


![image](https://github.com/user-attachments/assets/8913db0e-d6ec-4347-ad91-2329206b127c)

This pull request is essentially the work of @dsims in
https://github.com/discourse/discourse/pull/31761

---------

Co-authored-by: dsims <1041068+dsims@users.noreply.github.com>
This commit is contained in:
Joffrey JAFFEUX
2025-04-04 09:15:13 +02:00
committed by GitHub
parent 33748895a9
commit 4c8420833e
16 changed files with 431 additions and 40 deletions

View File

@ -266,8 +266,6 @@ end
# homepage_id :integer # homepage_id :integer
# theme_ids :integer default([]), not null, is an Array # theme_ids :integer default([]), not null, is an Array
# hide_profile_and_presence :boolean default(FALSE), not null # hide_profile_and_presence :boolean default(FALSE), not null
# hide_profile :boolean default(FALSE), not null
# hide_presence :boolean default(FALSE), not null
# text_size_key :integer default(0), not null # text_size_key :integer default(0), not null
# text_size_seq :integer default(0), not null # text_size_seq :integer default(0), not null
# email_level :integer default(1), not null # email_level :integer default(1), not null
@ -296,10 +294,14 @@ end
# sidebar_show_count_of_new_items :boolean default(FALSE), not null # sidebar_show_count_of_new_items :boolean default(FALSE), not null
# watched_precedence_over_muted :boolean # watched_precedence_over_muted :boolean
# chat_separate_sidebar_mode :integer default(0), not null # chat_separate_sidebar_mode :integer default(0), not null
# chat_send_shortcut :integer default(0), not null
# topics_unread_when_closed :boolean default(TRUE), not null # topics_unread_when_closed :boolean default(TRUE), not null
# show_thread_title_prompts :boolean default(TRUE), not null # show_thread_title_prompts :boolean default(TRUE), not null
# enable_smart_lists :boolean default(TRUE), not null # enable_smart_lists :boolean default(TRUE), not null
# hide_profile :boolean default(FALSE), not null
# hide_presence :boolean default(FALSE), not null
# chat_send_shortcut :integer default(0), not null
# chat_quick_reaction_type :integer default(0), not null
# chat_quick_reactions_custom :string
# #
# Indexes # Indexes
# #

View File

@ -19,8 +19,12 @@ const CHAT_ATTRS = [
"chat_header_indicator_preference", "chat_header_indicator_preference",
"chat_separate_sidebar_mode", "chat_separate_sidebar_mode",
"chat_send_shortcut", "chat_send_shortcut",
"chat_quick_reaction_type",
"chat_quick_reactions_custom",
]; ];
export const CHAT_QUICK_REACTIONS_CUSTOM_DEFAULT = "heart|+1|smile";
export const HEADER_INDICATOR_PREFERENCE_NEVER = "never"; export const HEADER_INDICATOR_PREFERENCE_NEVER = "never";
export const HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS = "dm_and_mentions"; export const HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS = "dm_and_mentions";
export const HEADER_INDICATOR_PREFERENCE_ALL_NEW = "all_new"; export const HEADER_INDICATOR_PREFERENCE_ALL_NEW = "all_new";
@ -32,6 +36,17 @@ export default class PreferencesChatController extends Controller {
subpageTitle = i18n("chat.admin.title"); subpageTitle = i18n("chat.admin.title");
chatQuickReactionTypes = [
{
label: i18n("chat.quick_reaction_type.options.frequent"),
value: "frequent",
},
{
label: i18n("chat.quick_reaction_type.options.custom"),
value: "custom",
},
];
chatSendShortcutOptions = [ chatSendShortcutOptions = [
{ {
label: i18n("chat.send_shortcut.enter.label"), label: i18n("chat.send_shortcut.enter.label"),
@ -99,6 +114,13 @@ export default class PreferencesChatController extends Controller {
return this.model.get("user_option.chat_send_shortcut"); return this.model.get("user_option.chat_send_shortcut");
} }
get chatQuickReactionsCustom() {
const emojis =
this.model.get("user_option.chat_quick_reactions_custom") ||
CHAT_QUICK_REACTIONS_CUSTOM_DEFAULT;
return emojis.split("|");
}
@discourseComputed @discourseComputed
chatSounds() { chatSounds() {
return Object.keys(CHAT_SOUNDS).map((value) => { return Object.keys(CHAT_SOUNDS).map((value) => {
@ -114,6 +136,24 @@ export default class PreferencesChatController extends Controller {
this.model.set("user_option.chat_sound", sound); this.model.set("user_option.chat_sound", sound);
} }
@action
onChangeQuickReactionType(value) {
this.model.set("user_option.chat_quick_reaction_type", value);
if (value === "custom") {
this.model.set(
"user_option.chat_quick_reactions_custom",
this.chatQuickReactionsCustom.join("|")
);
}
}
@action
didSelectEmoji(index, selected) {
let emoji = this.chatQuickReactionsCustom;
emoji[index] = selected;
this.model.set("user_option.chat_quick_reactions_custom", emoji.join("|"));
}
@action @action
save() { save() {
this.set("saved", false); this.set("saved", false);

View File

@ -9,6 +9,8 @@ const CHAT_EMAIL_FREQUENCY = "chat_email_frequency";
const CHAT_HEADER_INDICATOR_PREFERENCE = "chat_header_indicator_preference"; const CHAT_HEADER_INDICATOR_PREFERENCE = "chat_header_indicator_preference";
const CHAT_SEPARATE_SIDEBAR_MODE = "chat_separate_sidebar_mode"; const CHAT_SEPARATE_SIDEBAR_MODE = "chat_separate_sidebar_mode";
const CHAT_SEND_SHORTCUT = "chat_send_shortcut"; const CHAT_SEND_SHORTCUT = "chat_send_shortcut";
const CHAT_QUICK_REACTION_TYPE = "chat_quick_reaction_type";
const CHAT_QUICK_REACTIONS_CUSTOM = "chat_quick_reactions_custom";
export default { export default {
name: "chat-user-options", name: "chat-user-options",
@ -26,6 +28,8 @@ export default {
api.addSaveableUserOptionField(CHAT_HEADER_INDICATOR_PREFERENCE); api.addSaveableUserOptionField(CHAT_HEADER_INDICATOR_PREFERENCE);
api.addSaveableUserOptionField(CHAT_SEPARATE_SIDEBAR_MODE); api.addSaveableUserOptionField(CHAT_SEPARATE_SIDEBAR_MODE);
api.addSaveableUserOptionField(CHAT_SEND_SHORTCUT); api.addSaveableUserOptionField(CHAT_SEND_SHORTCUT);
api.addSaveableUserOptionField(CHAT_QUICK_REACTION_TYPE);
api.addSaveableUserOptionField(CHAT_QUICK_REACTIONS_CUSTOM);
} }
}); });
}, },

View File

@ -60,13 +60,28 @@ export default class ChatemojiReactions {
} }
get emojiReactions() { get emojiReactions() {
const defaultReactions = this.siteSettings.default_emoji_reactions const userQuickReactionsCustom = (
(this.currentUser.user_option.chat_quick_reaction_type === "custom" &&
this.currentUser.user_option.chat_quick_reactions_custom) ||
""
)
.split("|") .split("|")
.filter(Boolean); .filter(Boolean);
return this.emojiStore const frequentReactions = this.emojiStore.favoritesForContext("chat");
.favoritesForContext("chat")
.concat(defaultReactions) const defaultReactions =
this.siteSettings.default_emoji_reactions.split("|");
const allReactionsInOrder = userQuickReactionsCustom
.concat(frequentReactions)
.concat(defaultReactions);
return allReactionsInOrder
.filter((item, index) => {
return allReactionsInOrder.indexOf(item) === index;
})
.filter(Boolean)
.slice(0, 3) .slice(0, 3)
.map( .map(
(emoji) => (emoji) =>

View File

@ -1,8 +1,9 @@
import { Input } from "@ember/component"; import { Input } from "@ember/component";
import { concat, fn, hash } from "@ember/helper"; import { array, concat, fn, get, hash } from "@ember/helper";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import RouteTemplate from "ember-route-template"; import RouteTemplate from "ember-route-template";
import { eq } from "truth-helpers"; import { eq } from "truth-helpers";
import EmojiPicker from "discourse/components/emoji-picker";
import SaveControls from "discourse/components/save-controls"; import SaveControls from "discourse/components/save-controls";
import withEventValue from "discourse/helpers/with-event-value"; import withEventValue from "discourse/helpers/with-event-value";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
@ -26,6 +27,52 @@ export default RouteTemplate(
</label> </label>
</div> </div>
<fieldset
class="control-group chat-setting"
data-setting-name="user_chat_quick_reaction_type"
>
<legend class="control-label">{{i18n
"chat.quick_reaction_type.title"
}}</legend>
<div class="radio-group">
{{#each @controller.chatQuickReactionTypes as |option|}}
<div class="radio-group-option">
<label class="controls">
<input
type="radio"
name="user_chat_quick_reaction_type"
id={{concat "user_chat_quick_reaction_type_" option.value}}
value={{option.value}}
checked={{eq
@controller.model.user_option.chat_quick_reaction_type
option.value
}}
{{on
"change"
(withEventValue @controller.onChangeQuickReactionType)
}}
/>
{{option.label}}
</label>
</div>
{{/each}}
</div>
{{#if
(eq @controller.model.user_option.chat_quick_reaction_type "custom")
}}
<div class="controls tracking-controls emoji-pickers">
{{#each (array 0 1 2) as |index|}}
<EmojiPicker
@emoji={{get @controller.chatQuickReactionsCustom index}}
@didSelectEmoji={{fn @controller.didSelectEmoji index}}
@context="chat_preferences"
/>
{{/each}}
</div>
{{/if}}
</fieldset>
<div <div
class="control-group chat-setting" class="control-group chat-setting"
data-setting-name="user_chat_only_push_notifications" data-setting-name="user_chat_only_push_notifications"

View File

@ -674,6 +674,11 @@ en:
draft_channel_screen: draft_channel_screen:
header: "New Message" header: "New Message"
cancel: "Cancel" cancel: "Cancel"
quick_reaction_type:
title: "One-click chat reactions"
options:
frequent: "Most frequently used chat reactions"
custom: "Custom reactions"
notifications: notifications:
chat_invitation: "invited you to join a chat channel" chat_invitation: "invited you to join a chat channel"
chat_invitation_html: "<span>%{username}</span> <span>invited you to join a chat channel</span>" chat_invitation_html: "<span>%{username}</span> <span>invited you to join a chat channel</span>"

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddChatQuickReactionPreferences < ActiveRecord::Migration[7.2]
def change
add_column :user_options, :chat_quick_reaction_type, :integer, default: 0, null: false
add_column :user_options, :chat_quick_reactions_custom, :string
end
end

View File

@ -57,6 +57,10 @@ module Chat
if !base.method_defined?(:show_thread_title_prompts?) if !base.method_defined?(:show_thread_title_prompts?)
base.attribute :show_thread_title_prompts, :boolean, default: true base.attribute :show_thread_title_prompts, :boolean, default: true
end end
if !base.method_defined?(:chat_quick_reaction_type_frequent?)
base.enum :chat_quick_reaction_type, { frequent: 0, custom: 1 }, prefix: true
end
end end
end end
end end

View File

@ -54,6 +54,8 @@ after_initialize do
DiscoursePluginRegistry.register_flag_applies_to_type("Chat::Message", self) DiscoursePluginRegistry.register_flag_applies_to_type("Chat::Message", self)
UserUpdater::OPTION_ATTR.push(:chat_enabled) UserUpdater::OPTION_ATTR.push(:chat_enabled)
UserUpdater::OPTION_ATTR.push(:chat_quick_reaction_type)
UserUpdater::OPTION_ATTR.push(:chat_quick_reactions_custom)
UserUpdater::OPTION_ATTR.push(:only_chat_push_notifications) UserUpdater::OPTION_ATTR.push(:only_chat_push_notifications)
UserUpdater::OPTION_ATTR.push(:chat_sound) UserUpdater::OPTION_ATTR.push(:chat_sound)
UserUpdater::OPTION_ATTR.push(:ignore_channel_wide_mention) UserUpdater::OPTION_ATTR.push(:ignore_channel_wide_mention)
@ -251,6 +253,18 @@ after_initialize do
add_to_serializer(:current_user_option, :chat_send_shortcut) { object.chat_send_shortcut } add_to_serializer(:current_user_option, :chat_send_shortcut) { object.chat_send_shortcut }
add_to_serializer(:user_option, :chat_quick_reaction_type) { object.chat_quick_reaction_type }
add_to_serializer(:current_user_option, :chat_quick_reaction_type) do
object.chat_quick_reaction_type
end
add_to_serializer(:user_option, :chat_quick_reactions_custom) do
object.chat_quick_reactions_custom
end
add_to_serializer(:current_user_option, :chat_quick_reactions_custom) do
object.chat_quick_reactions_custom
end
on(:site_setting_changed) do |name, old_value, new_value| on(:site_setting_changed) do |name, old_value, new_value|
user_option_field = Chat::RETENTION_SETTINGS_TO_USER_OPTION_FIELDS[name.to_sym] user_option_field = Chat::RETENTION_SETTINGS_TO_USER_OPTION_FIELDS[name.to_sym]
begin begin

View File

@ -11,4 +11,9 @@ RSpec.describe UserOption do
expect(described_class.new.show_thread_title_prompts).to eq(true) expect(described_class.new.show_thread_title_prompts).to eq(true)
end end
end end
describe "#chat_quick_reaction_type" do
it "is present with frequent as default" do
expect(described_class.new.chat_quick_reaction_type).to eq("frequent")
end
end
end end

View File

@ -19,6 +19,26 @@ RSpec.describe CurrentUserSerializer do
end end
end end
describe "#chat_quick_reaction_type" do
it "is present with default enum string" do
expect(serializer.as_json[:user_option][:chat_quick_reaction_type]).to eq("frequent")
end
end
describe "#chat_quick_reactions_custom" do
it "is present with default enum string" do
expect(serializer.as_json[:user_option][:chat_quick_reactions_custom]).to eq(nil)
end
context "with custom quick reactions" do
before { current_user.user_option.update!(chat_quick_reactions_custom: "tada|smiley") }
it "is present" do
expect(serializer.as_json[:user_option][:chat_quick_reactions_custom]).to eq("tada|smiley")
end
end
end
describe "#chat_drafts" do describe "#chat_drafts" do
context "when user can't chat" do context "when user can't chat" do
before { SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff] } before { SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff] }

View File

@ -1,15 +1,33 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe UserSerializer do RSpec.describe UserSerializer do
fab!(:current_user) { Fabricate(:user) } fab!(:user)
let(:serializer) do let(:serializer) { described_class.new(user, scope: Guardian.new(user), root: false) }
described_class.new(current_user, scope: Guardian.new(current_user), root: false)
end
describe "#chat_separate_sidebar_mode" do describe "#chat_separate_sidebar_mode" do
it "is present" do it "is present" do
expect(serializer.as_json[:user_option][:chat_separate_sidebar_mode]).to eq("default") expect(serializer.as_json[:user_option][:chat_separate_sidebar_mode]).to eq("default")
end end
end end
describe "#chat_quick_reaction_type" do
it "is present with default enum string" do
expect(serializer.as_json[:user_option][:chat_quick_reaction_type]).to eq("frequent")
end
end
describe "#chat_quick_reactions_custom" do
it "is present with default enum string" do
expect(serializer.as_json[:user_option][:chat_quick_reactions_custom]).to eq(nil)
end
context "with custom quick reactions" do
before { user.user_option.update!(chat_quick_reactions_custom: "tada|smiley") }
it "is present" do
expect(serializer.as_json[:user_option][:chat_quick_reactions_custom]).to eq("tada|smiley")
end
end
end
end end

View File

@ -151,6 +151,10 @@ module PageObjects
within(message_reactions_list(message)) { return find("[data-emoji-name=\"#{emoji}\"]") } within(message_reactions_list(message)) { return find("[data-emoji-name=\"#{emoji}\"]") }
end end
def find_quick_reaction(emoji_name)
find(".chat-message-actions [data-emoji-name=\"#{emoji_name}\"]")
end
def has_reaction?(message, emoji, text = nil) def has_reaction?(message, emoji, text = nil)
within(message_reactions_list(message)) do within(message_reactions_list(message)) do
has_css?("[data-emoji-name=\"#{emoji}\"]", text: text) has_css?("[data-emoji-name=\"#{emoji}\"]", text: text)

View File

@ -0,0 +1,42 @@
# frozen_string_literal: true
module PageObjects
module Pages
class UserPreferencesChat < PageObjects::Pages::Base
def visit
page.visit("/my/preferences/chat")
self
end
def emoji_picker_triggers
all(".emoji-picker-trigger", visible: true)
end
def reaction_buttons
all(".emoji-pickers button")
end
def reactions_selected
reaction_buttons.map { |b| b.find("img")[:title] }
end
def select_option_value(selector, value)
select_kit = PageObjects::Components::SelectKit.new(selector)
select_kit.expand
select_kit.select_row_by_value(value)
end
def selected_option_value(selector)
PageObjects::Components::SelectKit.new(selector).value
end
def save_changes_and_refresh
page.find(".save-changes").click
# reloading the page happens in JS on save but capybara doesnt wait for it
# which can mess up navigating away too soon in tests
# so doing a manual refresh here to mimic
page.refresh
end
end
end
end

View File

@ -3,7 +3,10 @@
RSpec.describe "User chat preferences", type: :system do RSpec.describe "User chat preferences", type: :system do
fab!(:current_user) { Fabricate(:user) } fab!(:current_user) { Fabricate(:user) }
let(:user_preferences_chat_page) { PageObjects::Pages::UserPreferencesChat.new }
let(:emoji_picker) { PageObjects::Components::EmojiPicker.new }
let(:chat) { PageObjects::Pages::Chat.new } let(:chat) { PageObjects::Pages::Chat.new }
let(:channel) { PageObjects::Pages::ChatChannel.new }
before do before do
chat_system_bootstrap chat_system_bootstrap
@ -23,50 +26,89 @@ RSpec.describe "User chat preferences", type: :system do
end end
it "shows a not found page" do it "shows a not found page" do
visit("/my/preferences/chat") user_preferences_chat_page.visit
expect(page).to have_content(I18n.t("page_not_found.title")) expect(page).to have_content(I18n.t("page_not_found.title"))
end end
end end
it "can select chat sound" do it "can change chat quick reaction type to custom and select emoji" do
visit("/my/preferences") user_preferences_chat_page.visit
find(".user-nav__preferences-chat", visible: :all).click find("#user_chat_quick_reaction_type_custom").click
select_kit = PageObjects::Components::SelectKit.new("#user_chat_sounds")
select_kit.expand
select_kit.select_row_by_value("bell")
find(".save-changes").click
expect(select_kit).to have_selected_value("bell") expect(user_preferences_chat_page.emoji_picker_triggers.count).to eq 3
expect(user_preferences_chat_page.reactions_selected.first).to eq "heart"
user_preferences_chat_page.reaction_buttons.first.click
emoji_picker.select_emoji(":sweat_smile:")
user_preferences_chat_page.save_changes_and_refresh
expect(page).to have_checked_field("user_chat_quick_reaction_type_custom")
expect(user_preferences_chat_page.reactions_selected.first).to eq "sweat_smile"
end end
it "can select header_indicator_preference" do describe "chat interface" do
visit("/my/preferences") fab!(:category_channel_1) { Fabricate(:category_channel) }
find(".user-nav__preferences-chat", visible: :all).click fab!(:message_1) { Fabricate(:chat_message, chat_channel: category_channel_1) }
select_kit = PageObjects::Components::SelectKit.new("#user_chat_header_indicator_preference")
select_kit.expand
select_kit.select_row_by_value("dm_and_mentions")
find(".save-changes").click
expect(select_kit).to have_selected_value("dm_and_mentions") it "sees expected quick-reactions on hover" do
sign_in(current_user)
# save custom and look for reaction
user_preferences_chat_page.visit
find("#user_chat_quick_reaction_type_custom").click
user_preferences_chat_page.save_changes_and_refresh
chat.visit_channel(category_channel_1)
channel.hover_message(message_1)
expect(channel.find_quick_reaction("smile")).to be_present
# save frequent and look for reaction
user_preferences_chat_page.visit
find("#user_chat_quick_reaction_type_frequent").click
user_preferences_chat_page.save_changes_and_refresh
chat.visit_channel(category_channel_1)
channel.hover_message(message_1)
expect(channel.find_quick_reaction("tada")).to be_present
end
end end
it "can select separate sidebar mode" do shared_examples "select and save" do
visit("/my/preferences") it "can select and save" do
find(".user-nav__preferences-chat", visible: :all).click user_preferences_chat_page.visit
select_kit = PageObjects::Components::SelectKit.new("#user_chat_separate_sidebar_mode") user_preferences_chat_page.select_option_value(sel, val)
select_kit.expand user_preferences_chat_page.save_changes_and_refresh
select_kit.select_row_by_value("fullscreen")
find(".save-changes").click
expect(select_kit).to have_selected_value("fullscreen") expect(user_preferences_chat_page.selected_option_value(sel)).to eq val
end
end
describe "chat sound" do
include_examples "select and save" do
let(:sel) { "#user_chat_sounds" }
let(:val) { "bell" }
end
end
describe "header_indicator_preference" do
include_examples "select and save" do
let(:sel) { "#user_chat_header_indicator_preference" }
let(:val) { "dm_and_mentions" }
end
end
describe "separate sidebar mode" do
include_examples "select and save" do
let(:sel) { "#user_chat_separate_sidebar_mode" }
let(:val) { "fullscreen" }
end
end end
it "can select send shorcut sidebar mode" do it "can select send shorcut sidebar mode" do
visit("/my/preferences") user_preferences_chat_page.visit
find(".user-nav__preferences-chat", visible: :all).click
find("#chat_send_shortcut_meta_enter").click find("#chat_send_shortcut_meta_enter").click
find(".save-changes").click user_preferences_chat_page.save_changes_and_refresh
expect(page).to have_checked_field("chat_send_shortcut_meta_enter") expect(page).to have_checked_field("chat_send_shortcut_meta_enter")
end end

View File

@ -0,0 +1,122 @@
import { getOwner } from "@ember/owner";
import { setupTest } from "ember-qunit";
import { module, test } from "qunit";
import {
logIn,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import ChatMessageInteractor from "discourse/plugins/chat/discourse/lib/chat-message-interactor";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
module("Discourse Chat | Unit | chat-message-interactor", function (hooks) {
setupTest(hooks);
hooks.beforeEach(function () {
logIn(getOwner(this));
const message = new ChatFabricators(getOwner(this)).message();
this.messageInteractor = new ChatMessageInteractor(getOwner(this), message);
this.emojiStore = getOwner(this).lookup("service:emoji-store");
this.siteSettings = getOwner(this).lookup("service:site-settings");
});
test("emojiReactions with no option uses site default", function (assert) {
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["+1", "heart", "tada"]
);
});
test("emojiReactions empty when no frequent or site defaults", function (assert) {
this.siteSettings.default_emoji_reactions = "";
assert.deepEqual(this.messageInteractor.emojiReactions, []);
});
test("emojiReactions with user option frequent falls back to site defaults", function (assert) {
updateCurrentUser({
user_option: {
chat_quick_reaction_type: "frequent",
},
});
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["+1", "heart", "tada"]
);
});
test("emojiReactions with top 3 frequent", function (assert) {
this.emojiStore.trackEmojiForContext("eyes", "chat");
this.emojiStore.trackEmojiForContext("camera", "chat");
this.emojiStore.trackEmojiForContext("butterfly", "chat");
this.emojiStore.trackEmojiForContext("butterfly", "chat");
this.emojiStore.trackEmojiForContext("laptop", "chat");
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["butterfly", "laptop", "camera"]
);
});
test("emojiReactions with 1 frequent falls back to system", function (assert) {
this.emojiStore.trackEmojiForContext("butterfly", "chat");
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["butterfly", "+1", "heart"]
);
});
test("emojiReactions uses custom user option", function (assert) {
updateCurrentUser({
user_option: {
chat_quick_reaction_type: "custom",
chat_quick_reactions_custom: "grin|fearful|angry",
},
});
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["grin", "fearful", "angry"]
);
});
test("emojiReactions does not use custom if set to frequent", function (assert) {
updateCurrentUser({
user_option: {
chat_quick_reaction_type: "frequent",
chat_quick_reactions_custom: "grin|fearful|angry",
},
});
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["+1", "heart", "tada"]
);
});
test("emojiReactions avoids duplicates from frequent and site", function (assert) {
this.emojiStore.trackEmojiForContext("+1", "chat");
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["+1", "heart", "tada"]
);
});
test("emojiReactions avoids duplicates from custom + frequent + site", function (assert) {
updateCurrentUser({
user_option: {
chat_quick_reaction_type: "custom",
chat_quick_reactions_custom: "+1|+1|+1",
},
});
this.emojiStore.trackEmojiForContext("+1", "chat");
this.emojiStore.trackEmojiForContext("butterfly", "chat");
assert.deepEqual(
this.messageInteractor.emojiReactions.map((r) => r.emoji),
["+1", "butterfly", "heart"]
);
});
});