mirror of
https://github.com/discourse/discourse.git
synced 2025-03-20 13:06:39 +08:00
FEATURE: channels can allow/disallow @all/@here mentions (#19317)
The settings tab of each category channel should now present the option to allow or disallow channel wide mentions: @here and @all. When disallowed, using these mentions in the channel should have no effect.
This commit is contained in:
parent
569299b7a9
commit
68c4f16a73
@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
CHAT_CHANNEL_EDITABLE_PARAMS = %i[name description]
|
||||
CATEGORY_CHAT_CHANNEL_EDITABLE_PARAMS = %i[auto_join_users]
|
||||
CATEGORY_CHAT_CHANNEL_EDITABLE_PARAMS = %i[auto_join_users allow_channel_wide_mentions]
|
||||
|
||||
class Chat::Api::ChatChannelsController < Chat::Api
|
||||
def index
|
||||
|
@ -136,6 +136,7 @@ end
|
||||
# user_count :integer default(0), not null
|
||||
# last_message_sent_at :datetime not null
|
||||
# auto_join_users :boolean default(FALSE), not null
|
||||
# allow_channel_wide_mentions :boolean default(TRUE), not null
|
||||
# user_count_stale :boolean default(FALSE), not null
|
||||
# slug :string
|
||||
# type :string
|
||||
|
@ -3,6 +3,7 @@
|
||||
class ChatChannelSerializer < ApplicationSerializer
|
||||
attributes :id,
|
||||
:auto_join_users,
|
||||
:allow_channel_wide_mentions,
|
||||
:chatable,
|
||||
:chatable_id,
|
||||
:chatable_type,
|
||||
|
@ -0,0 +1,11 @@
|
||||
<span
|
||||
{{did-update this.activate @property}}
|
||||
{{will-destroy this.teardown}}
|
||||
class={{concat-class "chat-channel-settings-saved-indicator" (if this.isActive "is-active")}}
|
||||
role="status"
|
||||
>
|
||||
{{#if this.isActive}}
|
||||
{{d-icon "check"}}
|
||||
<span>{{i18n "saved"}}</span>
|
||||
{{/if}}
|
||||
</span>
|
@ -0,0 +1,28 @@
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { cancel } from "@ember/runloop";
|
||||
|
||||
const ACTIVE_DURATION = 2000;
|
||||
|
||||
export default class ChatChannelSettingsSavedIndicator extends Component {
|
||||
@tracked isActive = false;
|
||||
property = null;
|
||||
|
||||
@action
|
||||
activate() {
|
||||
cancel(this._deactivateHandler);
|
||||
|
||||
this.isActive = true;
|
||||
|
||||
this._deactivateHandler = discourseLater(() => {
|
||||
this.isActive = false;
|
||||
}, ACTIVE_DURATION);
|
||||
}
|
||||
|
||||
@action
|
||||
teardown() {
|
||||
cancel(this._deactivateHandler);
|
||||
}
|
||||
}
|
@ -2,58 +2,122 @@
|
||||
<div class="chat-form__field">
|
||||
<label class="chat-form__label">
|
||||
<span>{{i18n "chat.settings.mute"}}</span>
|
||||
<ChatChannelSettingsSavedIndicator
|
||||
@property={{this.channel.current_user_membership.muted}}
|
||||
/>
|
||||
</label>
|
||||
<div class="chat-form__control">
|
||||
<ComboBox @content={{this.mutedOptions}} @value={{this.channel.current_user_membership.muted}} @valueProperty="value" @class="channel-settings-view__muted-selector" @onChange={{action (fn this.saveNotificationSettings "muted")}} />
|
||||
{{#if this.savedMuted}}
|
||||
<span class="channel-settings-view__saved" role="status" aria-label={{i18n "chat.channel_settings.save_label.mute_channel"}}>
|
||||
{{d-icon "check"}} {{i18n "saved"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<ComboBox
|
||||
@content={{this.mutedOptions}}
|
||||
@value={{this.channel.current_user_membership.muted}}
|
||||
@valueProperty="value"
|
||||
@class="channel-settings-view__muted-selector"
|
||||
@onChange={{action (fn this.saveNotificationSettings "muted")}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#unless this.channel.current_user_membership.muted}}
|
||||
<div class="chat-form__field">
|
||||
<label class="chat-form__label">
|
||||
<span>{{i18n "chat.settings.desktop_notification_level"}}</span>
|
||||
<ChatChannelSettingsSavedIndicator
|
||||
@property={{this.channel.current_user_membership.desktop_notification_level}}
|
||||
/>
|
||||
</label>
|
||||
<div class="chat-form__control">
|
||||
<ComboBox @content={{this.notificationLevels}} @value={{this.channel.current_user_membership.desktop_notification_level}} @valueProperty="value" @class="channel-settings-view__desktop-notification-level-selector" @onChange={{action (fn this.saveNotificationSettings "desktop_notification_level")}} />
|
||||
{{#if this.savedDesktopNotificationLevel}}
|
||||
<span class="channel-settings-view__saved" role="status" aria-label={{i18n "chat.channel_settings.save_label.desktop_notification"}}>
|
||||
{{d-icon "check"}} {{i18n "saved"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<ComboBox
|
||||
@content={{this.notificationLevels}}
|
||||
@value={{this.channel.current_user_membership.desktop_notification_level}}
|
||||
@valueProperty="value"
|
||||
@class="channel-settings-view__desktop-notification-level-selector"
|
||||
@onChange={{action
|
||||
(fn this.saveNotificationSettings "desktop_notification_level")
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-form__field">
|
||||
<label class="chat-form__label">
|
||||
<span>{{i18n "chat.settings.mobile_notification_level"}}</span>
|
||||
<ChatChannelSettingsSavedIndicator
|
||||
@property={{this.channel.current_user_membership.mobile_notification_level}}
|
||||
/>
|
||||
</label>
|
||||
<div class="chat-form__control">
|
||||
<ComboBox @content={{this.notificationLevels}} @value={{this.channel.current_user_membership.mobile_notification_level}} @valueProperty="value" @class="channel-settings-view__mobile-notification-level-selector" @onChange={{action (fn this.saveNotificationSettings "mobile_notification_level")}} />
|
||||
{{#if this.savedMobileNotificationLevel}}
|
||||
<span class="channel-settings-view__saved" role="status" aria-label={{i18n "chat.channel_settings.save_label.mobile_notification"}}>
|
||||
{{d-icon "check"}} {{i18n "saved"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<ComboBox
|
||||
@content={{this.notificationLevels}}
|
||||
@value={{this.channel.current_user_membership.mobile_notification_level}}
|
||||
@valueProperty="value"
|
||||
@class="channel-settings-view__mobile-notification-level-selector"
|
||||
@onChange={{action
|
||||
(fn this.saveNotificationSettings "mobile_notification_level")
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
<div class="chat-retention-info">{{d-icon "info-circle"}}{{i18n "chat.settings.retention_info" days=this.siteSettings.chat_channel_retention_days}}</div>
|
||||
<div class="chat-retention-info">
|
||||
{{d-icon "info-circle"}}
|
||||
{{i18n
|
||||
"chat.settings.retention_info"
|
||||
days=this.siteSettings.chat_channel_retention_days
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if (chat-guardian "can-edit-chat-channel")}}
|
||||
<h3 class="chat-form__section-admin-title">{{i18n "chat.settings.admin_title"}}</h3>
|
||||
{{#if this.adminSectionAvailable}}
|
||||
<h3 class="chat-form__section-admin-title">
|
||||
{{i18n "chat.settings.admin_title"}}
|
||||
</h3>
|
||||
{{#if this.autoJoinAvailable}}
|
||||
<div class="chat-form__section">
|
||||
<div class="chat-form__field">
|
||||
<label class="chat-form__label">
|
||||
<span>{{i18n "chat.settings.auto_join_users_label"}}</span>
|
||||
<ChatChannelSettingsSavedIndicator
|
||||
@property={{this.channel.auto_join_users}}
|
||||
/>
|
||||
</label>
|
||||
<ComboBox @content={{this.autoAddUsersOptions}} @value={{this.channel.auto_join_users}} @valueProperty="value" @class="channel-settings-view__auto-join-selector" @onChange={{action (fn this.onToggleAutoJoinUsers this.channel.auto_join_users)}} />
|
||||
<div class="chat-form__description -autojoin">{{i18n "chat.settings.auto_join_users_info" category=this.channel.chatable.name}}</div>
|
||||
<ComboBox
|
||||
@content={{this.autoAddUsersOptions}}
|
||||
@value={{this.channel.auto_join_users}}
|
||||
@valueProperty="value"
|
||||
@class="channel-settings-view__auto-join-selector"
|
||||
@onChange={{action
|
||||
(fn this.onToggleAutoJoinUsers this.channel.auto_join_users)
|
||||
}}
|
||||
/>
|
||||
<p class="chat-form__description -autojoin">
|
||||
{{i18n
|
||||
"chat.settings.auto_join_users_info"
|
||||
category=this.channel.chatable.name
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.togglingChannelWideMentionsAvailable}}
|
||||
<div class="chat-form__section">
|
||||
<div class="chat-form__field">
|
||||
<label class="chat-form__label">
|
||||
<span>{{i18n "chat.settings.channel_wide_mentions_label"}}</span>
|
||||
<ChatChannelSettingsSavedIndicator
|
||||
@property={{this.channel.allow_channel_wide_mentions}}
|
||||
/>
|
||||
</label>
|
||||
<ComboBox
|
||||
@content={{this.channelWideMentionsOptions}}
|
||||
@value={{this.channel.allow_channel_wide_mentions}}
|
||||
@valueProperty="value"
|
||||
@class="channel-settings-view__channel-wide-mentions-selector"
|
||||
@onChange={{this.onToggleChannelWideMentions}}
|
||||
/>
|
||||
<p class="chat-form__description -channel-wide-mentions">
|
||||
{{i18n "chat.settings.channel_wide_mentions_description" channel=this.channel.title}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -64,22 +128,42 @@
|
||||
{{#if (chat-guardian "can-edit-chat-channel")}}
|
||||
{{#if (chat-guardian "can-archive-channel" this.channel)}}
|
||||
<div class="chat-form__field">
|
||||
<DButton @action={{action "onArchiveChannel"}} @label="chat.channel_settings.archive_channel" @class="archive-btn chat-form__btn btn-flat" @icon="archive" />
|
||||
<DButton
|
||||
@action={{action "onArchiveChannel"}}
|
||||
@label="chat.channel_settings.archive_channel"
|
||||
@class="archive-btn chat-form__btn btn-flat"
|
||||
@icon="archive"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.channel.isClosed}}
|
||||
<div class="chat-form__field">
|
||||
<DButton @action={{action "onToggleChannelState"}} @label="chat.channel_settings.open_channel" @class="open-btn chat-form__btn btn-flat" @icon="unlock" />
|
||||
<DButton
|
||||
@action={{action "onToggleChannelState"}}
|
||||
@label="chat.channel_settings.open_channel"
|
||||
@class="open-btn chat-form__btn btn-flat"
|
||||
@icon="unlock"
|
||||
/>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="chat-form__field">
|
||||
<DButton @action={{action "onToggleChannelState"}} @label="chat.channel_settings.close_channel" @class="close-btn chat-form__btn btn-flat" @icon="lock" />
|
||||
<DButton
|
||||
@action={{action "onToggleChannelState"}}
|
||||
@label="chat.channel_settings.close_channel"
|
||||
@class="close-btn chat-form__btn btn-flat"
|
||||
@icon="lock"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="chat-form__field">
|
||||
<DButton @action={{action "onDeleteChannel"}} @label="chat.channel_settings.delete_channel" @class="delete-btn chat-form__btn btn-flat" @icon="trash-alt" />
|
||||
<DButton
|
||||
@action={{action "onDeleteChannel"}}
|
||||
@label="chat.channel_settings.delete_channel"
|
||||
@class="delete-btn chat-form__btn btn-flat"
|
||||
@icon="trash-alt"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
@ -4,8 +4,8 @@ import { inject as service } from "@ember/service";
|
||||
import ChatApi from "discourse/plugins/chat/discourse/lib/chat-api";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import I18n from "I18n";
|
||||
import { camelize } from "@ember/string";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import { Promise } from "rsvp";
|
||||
import { reads } from "@ember/object/computed";
|
||||
|
||||
const NOTIFICATION_LEVELS = [
|
||||
{ name: I18n.t("chat.notification_levels.never"), value: "never" },
|
||||
@ -23,8 +23,17 @@ const AUTO_ADD_USERS_OPTIONS = [
|
||||
{ name: I18n.t("no_value"), value: false },
|
||||
];
|
||||
|
||||
const CHANNEL_WIDE_MENTIONS_OPTIONS = [
|
||||
{ name: I18n.t("yes_value"), value: true },
|
||||
{
|
||||
name: I18n.t("no_value"),
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
|
||||
export default class ChatChannelSettingsView extends Component {
|
||||
@service chat;
|
||||
@service chatGuardian;
|
||||
@service router;
|
||||
@service dialog;
|
||||
tagName = "";
|
||||
@ -33,57 +42,28 @@ export default class ChatChannelSettingsView extends Component {
|
||||
notificationLevels = NOTIFICATION_LEVELS;
|
||||
mutedOptions = MUTED_OPTIONS;
|
||||
autoAddUsersOptions = AUTO_ADD_USERS_OPTIONS;
|
||||
channelWideMentionsOptions = CHANNEL_WIDE_MENTIONS_OPTIONS;
|
||||
isSavingNotificationSetting = false;
|
||||
savedDesktopNotificationLevel = false;
|
||||
savedMobileNotificationLevel = false;
|
||||
savedMuted = false;
|
||||
|
||||
_updateAutoJoinUsers(value) {
|
||||
return ChatApi.modifyChatChannel(this.channel.id, {
|
||||
auto_join_users: value,
|
||||
})
|
||||
.then((chatChannel) => {
|
||||
this.channel.set("auto_join_users", chatChannel.auto_join_users);
|
||||
})
|
||||
.catch((event) => {
|
||||
if (event.jqXHR?.responseJSON?.errors) {
|
||||
this.flash(event.jqXHR.responseJSON.errors.join("\n"), "error");
|
||||
}
|
||||
});
|
||||
@reads("channel.isCategoryChannel") togglingChannelWideMentionsAvailable;
|
||||
|
||||
@computed("channel.isCategoryChannel")
|
||||
get autoJoinAvailable() {
|
||||
return (
|
||||
this.siteSettings.max_chat_auto_joined_users > 0 &&
|
||||
this.channel.isCategoryChannel
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
saveNotificationSettings(key, value) {
|
||||
if (this.channel[key] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const camelizedKey = camelize(`saved_${key}`);
|
||||
this.set(camelizedKey, false);
|
||||
|
||||
const settings = {};
|
||||
settings[key] = value;
|
||||
return ChatApi.updateChatChannelNotificationsSettings(
|
||||
this.channel.id,
|
||||
settings
|
||||
)
|
||||
.then((membership) => {
|
||||
this.channel.current_user_membership.setProperties({
|
||||
muted: membership.muted,
|
||||
desktop_notification_level: membership.desktop_notification_level,
|
||||
mobile_notification_level: membership.mobile_notification_level,
|
||||
});
|
||||
this.set(camelizedKey, true);
|
||||
})
|
||||
.finally(() => {
|
||||
discourseLater(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set(camelizedKey, false);
|
||||
}, 2000);
|
||||
});
|
||||
@computed("autoJoinAvailable", "togglingChannelWideMentionsAvailable")
|
||||
get adminSectionAvailable() {
|
||||
return (
|
||||
this.chatGuardian.canEditChatChannel &&
|
||||
(this.autoJoinAvailable || this.togglingChannelWideMentionsAvailable)
|
||||
);
|
||||
}
|
||||
|
||||
@computed(
|
||||
@ -98,12 +78,24 @@ export default class ChatChannelSettingsView extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
@computed("channel.isCategoryChannel")
|
||||
get autoJoinAvailable() {
|
||||
return (
|
||||
this.siteSettings.max_chat_auto_joined_users > 0 &&
|
||||
this.channel.isCategoryChannel
|
||||
);
|
||||
@action
|
||||
saveNotificationSettings(key, value) {
|
||||
if (this.channel[key] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = {};
|
||||
settings[key] = value;
|
||||
return ChatApi.updateChatChannelNotificationsSettings(
|
||||
this.channel.id,
|
||||
settings
|
||||
).then((membership) => {
|
||||
this.channel.current_user_membership.setProperties({
|
||||
muted: membership.muted,
|
||||
desktop_notification_level: membership.desktop_notification_level,
|
||||
mobile_notification_level: membership.mobile_notification_level,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
@ -133,8 +125,17 @@ export default class ChatChannelSettingsView extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
onToggleChannelWideMentions() {
|
||||
return this._updateChannelProperty(
|
||||
this.channel,
|
||||
"allow_channel_wide_mentions",
|
||||
!this.channel.allow_channel_wide_mentions
|
||||
);
|
||||
}
|
||||
|
||||
onDisableAutoJoinUsers() {
|
||||
this._updateAutoJoinUsers(false);
|
||||
return this._updateChannelProperty(this.channel, "auto_join_users", false);
|
||||
}
|
||||
|
||||
onEnableAutoJoinUsers() {
|
||||
@ -142,7 +143,26 @@ export default class ChatChannelSettingsView extends Component {
|
||||
message: I18n.t("chat.settings.auto_join_users_warning", {
|
||||
category: this.channel.chatable.name,
|
||||
}),
|
||||
didConfirm: () => this._updateAutoJoinUsers(true),
|
||||
didConfirm: () =>
|
||||
this._updateChannelProperty(this.channel, "auto_join_users", true),
|
||||
});
|
||||
}
|
||||
|
||||
_updateChannelProperty(channel, property, value) {
|
||||
if (channel[property] === value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const payload = {};
|
||||
payload[property] = value;
|
||||
return ChatApi.modifyChatChannel(channel.id, payload)
|
||||
.then((updatedChannel) => {
|
||||
channel.set(property, updatedChannel[property]);
|
||||
})
|
||||
.catch((event) => {
|
||||
if (event.jqXHR?.responseJSON?.errors) {
|
||||
this.flash(event.jqXHR.responseJSON.errors.join("\n"), "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -130,15 +130,15 @@ export default {
|
||||
|
||||
api.addToHeaderIcons("header-chat-link");
|
||||
|
||||
api.decorateChatMessage(function (chatMessage) {
|
||||
api.decorateChatMessage(function (chatMessage, chatChannel) {
|
||||
if (!this.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const highlightable = [
|
||||
`@${this.currentUser.username}`,
|
||||
...MENTION_KEYWORDS.map((k) => `@${k}`),
|
||||
];
|
||||
const highlightable = [`@${this.currentUser.username}`];
|
||||
if (chatChannel.allow_channel_wide_mentions) {
|
||||
highlightable.push(...MENTION_KEYWORDS.map((k) => `@${k}`));
|
||||
}
|
||||
|
||||
chatMessage.querySelectorAll(".mention").forEach((node) => {
|
||||
const mention = node.textContent.trim();
|
||||
|
@ -32,20 +32,11 @@
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
// Settings view
|
||||
.channel-settings-view__saved {
|
||||
color: var(--success);
|
||||
padding-left: 0.5rem;
|
||||
|
||||
.d-icon-check {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.channel-settings-view__desktop-notification-level-selector,
|
||||
.channel-settings-view__mobile-notification-level-selector,
|
||||
.channel-settings-view__muted-selector,
|
||||
.channel-settings-view__auto-join-selector {
|
||||
.channel-settings-view__auto-join-selector,
|
||||
.channel-settings-view__channel-wide-mentions-selector {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
.chat-channel-settings-saved-indicator {
|
||||
padding-left: 0.5rem;
|
||||
color: var(--success);
|
||||
font-weight: normal;
|
||||
|
||||
.d-icon-check {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
@ -148,7 +148,8 @@
|
||||
}
|
||||
|
||||
.chat-form {
|
||||
&__description.-autojoin {
|
||||
&__description.-autojoin,
|
||||
&__description.-channel-wide-mentions {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
@ -289,6 +289,8 @@ en:
|
||||
always: "For all activity"
|
||||
|
||||
settings:
|
||||
channel_wide_mentions_label: "Allow @all and @here mentions"
|
||||
channel_wide_mentions_description: "Allow users to notify all members of #%{channel} with @all or only those who are active in the moment with @here"
|
||||
auto_join_users_label: "Automatically add users"
|
||||
auto_join_users_info: "Check hourly which users have been active in the last 3 months and, if they have access to the %{category} category, add them to this channel."
|
||||
enable_auto_join_users: "Automatically add all recently active users"
|
||||
|
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddAllowChannelWideMentionsToChatChannels < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :chat_channels, :allow_channel_wide_mentions, :boolean, null: false, default: true
|
||||
end
|
||||
end
|
@ -164,7 +164,7 @@ class Chat::ChatNotifier
|
||||
def expand_global_mention(to_notify, already_covered_ids)
|
||||
typed_global_mention = direct_mentions_from_cooked.include?("@all")
|
||||
|
||||
if typed_global_mention
|
||||
if typed_global_mention && @chat_channel.allow_channel_wide_mentions
|
||||
to_notify[:global_mentions] = members_accepting_channel_wide_notifications
|
||||
.where.not(username_lower: normalized_mentions(direct_mentions_from_cooked))
|
||||
.where.not(id: already_covered_ids)
|
||||
@ -179,7 +179,7 @@ class Chat::ChatNotifier
|
||||
def expand_here_mention(to_notify, already_covered_ids)
|
||||
typed_here_mention = direct_mentions_from_cooked.include?("@here")
|
||||
|
||||
if typed_here_mention
|
||||
if typed_here_mention && @chat_channel.allow_channel_wide_mentions
|
||||
to_notify[:here_mentions] = members_accepting_channel_wide_notifications
|
||||
.where("last_seen_at > ?", 5.minutes.ago)
|
||||
.where.not(username_lower: normalized_mentions(direct_mentions_from_cooked))
|
||||
|
@ -66,6 +66,7 @@ register_asset "stylesheets/common/chat-onebox.scss"
|
||||
register_asset "stylesheets/common/chat-skeleton.scss"
|
||||
register_asset "stylesheets/colors.scss", :color_definitions
|
||||
register_asset "stylesheets/common/reviewable-chat-message.scss"
|
||||
register_asset "stylesheets/common/chat-channel-settings-saved-indicator.scss"
|
||||
|
||||
register_svg_icon "comments"
|
||||
register_svg_icon "comment-slash"
|
||||
|
@ -49,6 +49,15 @@ describe Chat::ChatNotifier do
|
||||
expect(to_notify[list_key]).to be_empty
|
||||
end
|
||||
|
||||
it "will never mention when channel is not accepting channel wide mentions" do
|
||||
channel.update!(allow_channel_wide_mentions: false)
|
||||
msg = build_cooked_msg(mention, user_1)
|
||||
|
||||
to_notify = described_class.new(msg, msg.created_at).notify_new
|
||||
|
||||
expect(to_notify[list_key]).to be_empty
|
||||
end
|
||||
|
||||
it "includes all members of a channel except the sender" do
|
||||
msg = build_cooked_msg(mention, user_1)
|
||||
|
||||
|
@ -13,13 +13,25 @@ RSpec.describe ChatChannel do
|
||||
end
|
||||
|
||||
context "when the slug is not nil" do
|
||||
before do
|
||||
category_channel.update!(slug: "some-cool-channel")
|
||||
end
|
||||
before { category_channel.update!(slug: "some-cool-channel") }
|
||||
|
||||
it "includes the slug for the channel" do
|
||||
expect(category_channel.relative_url).to eq("/chat/channel/#{category_channel.id}/some-cool-channel")
|
||||
expect(category_channel.relative_url).to eq(
|
||||
"/chat/channel/#{category_channel.id}/some-cool-channel",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#allow_channel_wide_mentions" do
|
||||
it "defaults to true" do
|
||||
expect(category_channel.allow_channel_wide_mentions).to be(true)
|
||||
end
|
||||
|
||||
it "cant be nullified" do
|
||||
expect { category_channel.update!(allow_channel_wide_mentions: nil) }.to raise_error(
|
||||
ActiveRecord::NotNullViolation,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -277,6 +277,17 @@ describe Chat::Api::ChatChannelsController do
|
||||
expect(response.parsed_body).to match_response_schema("category_chat_channel")
|
||||
end
|
||||
|
||||
describe "when updating allow_channel_wide_mentions" do
|
||||
it "sets the new value" do
|
||||
put "/chat/api/chat_channels/#{chat_channel.id}.json",
|
||||
params: {
|
||||
allow_channel_wide_mentions: false,
|
||||
}
|
||||
|
||||
expect(response.parsed_body["allow_channel_wide_mentions"]).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Updating a channel to add users automatically" do
|
||||
it "sets the channel to auto-update users automatically" do
|
||||
put "/chat/api/chat_channels/#{chat_channel.id}.json", params: { auto_join_users: true }
|
||||
|
@ -17,6 +17,10 @@ describe ChatChannelSerializer do
|
||||
it "does not return any sort of archive status" do
|
||||
expect(subject.as_json.key?(:archive_completed)).to eq(false)
|
||||
end
|
||||
|
||||
it "includes allow_channel_wide_mentions" do
|
||||
expect(subject.as_json.key?(:allow_channel_wide_mentions)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user is staff" do
|
||||
@ -37,6 +41,10 @@ describe ChatChannelSerializer do
|
||||
chat_channel.reload
|
||||
expect(subject.as_json.key?(:archive_completed)).to eq(true)
|
||||
end
|
||||
|
||||
it "includes allow_channel_wide_mentions" do
|
||||
expect(subject.as_json.key?(:allow_channel_wide_mentions)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -31,6 +31,7 @@ export const directMessageChannels = [
|
||||
muted: false,
|
||||
following: true,
|
||||
},
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-20T08:14:16.950Z",
|
||||
message_bus_last_ids: {
|
||||
new_mentions: 0,
|
||||
@ -66,6 +67,7 @@ export const directMessageChannels = [
|
||||
muted: false,
|
||||
following: true,
|
||||
},
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-05T12:04:00.850Z",
|
||||
message_bus_last_ids: {
|
||||
new_mentions: 0,
|
||||
@ -107,6 +109,7 @@ export const chatChannels = {
|
||||
title: "Site",
|
||||
status: "open",
|
||||
chatable: chatables[1],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-24T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
@ -126,6 +129,7 @@ export const chatChannels = {
|
||||
title: "Bug",
|
||||
status: "open",
|
||||
chatable: chatables[1],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-15T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
@ -145,6 +149,7 @@ export const chatChannels = {
|
||||
title: "Public category",
|
||||
status: "open",
|
||||
chatable: chatables[8],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-14T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
@ -164,6 +169,7 @@ export const chatChannels = {
|
||||
title: "Public category (read-only)",
|
||||
status: "read_only",
|
||||
chatable: chatables[8],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-10T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
@ -183,6 +189,7 @@ export const chatChannels = {
|
||||
title: "Public category (closed)",
|
||||
status: "closed",
|
||||
chatable: chatables[8],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-21T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
@ -202,6 +209,7 @@ export const chatChannels = {
|
||||
title: "Public category (archived)",
|
||||
status: "archived",
|
||||
chatable: chatables[8],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-25T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
@ -221,6 +229,7 @@ export const chatChannels = {
|
||||
title: "Another Category",
|
||||
status: "open",
|
||||
chatable: chatables[12],
|
||||
allow_channel_wide_mentions: true,
|
||||
last_message_sent_at: "2021-07-02T08:14:16.950Z",
|
||||
current_user_membership: {
|
||||
unread_count: 0,
|
||||
|
@ -0,0 +1,31 @@
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { render, settled } from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
|
||||
module(
|
||||
"Discourse Chat | Component | chat-channel-settings-saved-indicator",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("when property changes", async function (assert) {
|
||||
await render(
|
||||
hbs`<ChatChannelSettingsSavedIndicator @property={{this.property}} />`
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".chat-channel-settings-saved-indicator.is-active")
|
||||
.doesNotExist();
|
||||
|
||||
this.set("property", 1);
|
||||
|
||||
assert.dom(".chat-channel-settings-saved-indicator.is-active").exists();
|
||||
|
||||
await settled();
|
||||
|
||||
assert
|
||||
.dom(".chat-channel-settings-saved-indicator.is-active")
|
||||
.doesNotExist();
|
||||
});
|
||||
}
|
||||
);
|
@ -9,7 +9,14 @@ import { CHATABLE_TYPES } from "discourse/plugins/chat/discourse/models/chat-cha
|
||||
import { module } from "qunit";
|
||||
|
||||
function membershipFixture(id, options = {}) {
|
||||
options = Object.assign({}, options, { muted: false, following: true });
|
||||
options = Object.assign(
|
||||
{},
|
||||
{
|
||||
muted: false,
|
||||
following: true,
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
return {
|
||||
following: options.following,
|
||||
@ -99,7 +106,7 @@ module(
|
||||
return [
|
||||
200,
|
||||
{ "Content-Type": "application/json" },
|
||||
membershipFixture(this.channel.id, { muted: true }),
|
||||
membershipFixture(this.channel.id, { muted: false }),
|
||||
];
|
||||
}
|
||||
);
|
||||
@ -111,6 +118,34 @@ module(
|
||||
assert.equal(sk.header().value(), "false");
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("allow channel wide mentions", {
|
||||
template: hbs`{{chat-channel-settings-view channel=channel}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set("channel", fabricators.chatChannel());
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
pretender.put(`/chat/api/chat_channels/${this.channel.id}.json`, () => {
|
||||
return [
|
||||
200,
|
||||
{ "Content-Type": "application/json" },
|
||||
{
|
||||
allow_channel_wide_mentions: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const sk = selectKit(
|
||||
".channel-settings-view__channel-wide-mentions-selector"
|
||||
);
|
||||
await sk.expand();
|
||||
await sk.selectRowByName("No");
|
||||
|
||||
assert.equal(sk.header().value(), "false");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@ -205,7 +240,7 @@ module(
|
||||
return [
|
||||
200,
|
||||
{ "Content-Type": "application/json" },
|
||||
membershipFixture(this.channel.id, { muted: true }),
|
||||
membershipFixture(this.channel.id, { muted: false }),
|
||||
];
|
||||
}
|
||||
);
|
||||
@ -217,5 +252,24 @@ module(
|
||||
assert.equal(sk.header().value(), "false");
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("allow channel wide mentions", {
|
||||
template: hbs`{{chat-channel-settings-view channel=channel}}`,
|
||||
|
||||
beforeEach() {
|
||||
this.set(
|
||||
"channel",
|
||||
fabricators.chatChannel({
|
||||
chatable_type: CHATABLE_TYPES.directMessageChannel,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
async test(assert) {
|
||||
assert
|
||||
.dom(".channel-settings-view__channel-wide-mentions-selector")
|
||||
.doesNotExist();
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -31,6 +31,7 @@ export default {
|
||||
name: "My category name",
|
||||
chatable: categoryChatableFabricator(),
|
||||
last_message_sent_at: "2021-11-08T21:26:05.710Z",
|
||||
allow_channel_wide_mentions: true,
|
||||
message_bus_last_ids: {
|
||||
new_mentions: 0,
|
||||
new_messages: 0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user