mirror of
https://github.com/discourse/discourse.git
synced 2025-05-29 23:58:08 +08:00
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 `😄`.  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:
@ -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
|
||||||
#
|
#
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -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) =>
|
||||||
|
@ -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"
|
||||||
|
@ -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>"
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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] }
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue
Block a user