DEV: Use Notice API for mention warnings (#23238)
This PR swaps out the custom pathway to publishing and rendering mention warnings after a message is sent. ChatPublisher#publish_notice is used, and expanded. Now, instead of only accepting text_content as an argument, component and component_args are accepted and there is a renderer for these components. Translations moved to server, as notices expect text to be passed in unless a component is rendered The warnings are rendered at the top now, outside of the scope of the single message that sent it. I entirely removed the jit_messages_spec b/c it's duplicate testing of other parts of the app. IMO we don't need a backend test for a feature, a component test for the feature AND a system test (that is slow and potentially even flakey due to timing issues with wait) to test the same thing. So jit_messages_spec is gone.
This commit is contained in:
parent
ed35ae4dcd
commit
9c65e2140a
|
@ -387,31 +387,6 @@ module Chat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.publish_inaccessible_mentions(
|
|
||||||
user_id,
|
|
||||||
chat_message,
|
|
||||||
cannot_chat_users,
|
|
||||||
without_membership,
|
|
||||||
too_many_members,
|
|
||||||
mentions_disabled,
|
|
||||||
global_mentions_disabled
|
|
||||||
)
|
|
||||||
MessageBus.publish(
|
|
||||||
"/chat/#{chat_message.chat_channel_id}",
|
|
||||||
{
|
|
||||||
type: :mention_warning,
|
|
||||||
chat_message_id: chat_message.id,
|
|
||||||
cannot_see: cannot_chat_users.map { |u| { username: u.username, id: u.id } }.as_json,
|
|
||||||
without_membership:
|
|
||||||
without_membership.map { |u| { username: u.username, id: u.id } }.as_json,
|
|
||||||
groups_with_too_many_members: too_many_members.map(&:name).as_json,
|
|
||||||
group_mentions_disabled: mentions_disabled.map(&:name).as_json,
|
|
||||||
global_mentions_disabled: global_mentions_disabled,
|
|
||||||
},
|
|
||||||
user_ids: [user_id],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.publish_kick_users(channel_id, user_ids)
|
def self.publish_kick_users(channel_id, user_ids)
|
||||||
MessageBus.publish(
|
MessageBus.publish(
|
||||||
kick_users_message_bus_channel(channel_id),
|
kick_users_message_bus_channel(channel_id),
|
||||||
|
@ -478,8 +453,19 @@ module Chat
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.publish_notice(user_id:, channel_id:, text_content:)
|
def self.publish_notice(user_id:, channel_id:, text_content: nil, type: nil, data: nil)
|
||||||
payload = { type: "notice", text_content: text_content, channel_id: channel_id }
|
# Notices are either plain text sent to the client, or a "type" with data. The
|
||||||
|
# client will then translate that type and data into a front-end component.
|
||||||
|
if text_content.blank? && type.blank? && data.blank?
|
||||||
|
raise "Cannot publish notice without text content or a type"
|
||||||
|
end
|
||||||
|
payload = { type: "notice", channel_id: channel_id }
|
||||||
|
if text_content
|
||||||
|
payload[:text_content] = text_content
|
||||||
|
else
|
||||||
|
payload[:notice_type] = type
|
||||||
|
payload[:data] = data
|
||||||
|
end
|
||||||
|
|
||||||
MessageBus.publish("/chat/#{channel_id}", payload, user_ids: [user_id])
|
MessageBus.publish("/chat/#{channel_id}", payload, user_ids: [user_id])
|
||||||
end
|
end
|
||||||
|
|
|
@ -118,7 +118,6 @@
|
||||||
@message={{@message}}
|
@message={{@message}}
|
||||||
@onRetry={{@resendStagedMessage}}
|
@onRetry={{@resendStagedMessage}}
|
||||||
/>
|
/>
|
||||||
<Chat::Message::MentionWarning @message={{@message}} />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.showThreadIndicator}}
|
{{#if this.showThreadIndicator}}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="chat-notices__notice">
|
||||||
|
|
||||||
|
{{#if @notice.textContent}}
|
||||||
|
<p class="chat-notices__notice__content">
|
||||||
|
{{@notice.textContent}}
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<this.component
|
||||||
|
@channel={{@channel}}
|
||||||
|
@notice={{@notice}}
|
||||||
|
@clearNotice={{this.clearNotice}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@icon="times"
|
||||||
|
@action={{this.clearNotice}}
|
||||||
|
class="btn-flat chat-notices__notice__clear"
|
||||||
|
/>
|
||||||
|
</div>
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import MentionWithoutMembership from "discourse/plugins/chat/discourse/components/chat/notices/mention_without_membership";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
|
const COMPONENT_DICT = {
|
||||||
|
mention_without_membership: MentionWithoutMembership,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class ChatNotices extends Component {
|
||||||
|
@service("chat-channel-pane-subscriptions-manager") subscriptionsManager;
|
||||||
|
|
||||||
|
@action
|
||||||
|
clearNotice() {
|
||||||
|
this.subscriptionsManager.clearNotice(this.args.notice);
|
||||||
|
}
|
||||||
|
|
||||||
|
get component() {
|
||||||
|
return COMPONENT_DICT[this.args.notice.type];
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,16 +2,6 @@
|
||||||
<ChatRetentionReminder @channel={{@channel}} />
|
<ChatRetentionReminder @channel={{@channel}} />
|
||||||
|
|
||||||
{{#each this.noticesForChannel as |notice|}}
|
{{#each this.noticesForChannel as |notice|}}
|
||||||
<div class="chat-notices__notice">
|
<ChatNotice @notice={{notice}} @channel={{@channel}} />
|
||||||
<p class="chat-notices__notice__content">
|
|
||||||
{{notice.textContent}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@icon="times"
|
|
||||||
@action={{fn this.clearNotice notice}}
|
|
||||||
class="btn-flat chat-notices__notice__clear"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
|
@ -1,6 +1,5 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { action } from "@ember/object";
|
|
||||||
|
|
||||||
export default class ChatNotices extends Component {
|
export default class ChatNotices extends Component {
|
||||||
@service("chat-channel-pane-subscriptions-manager") subscriptionsManager;
|
@service("chat-channel-pane-subscriptions-manager") subscriptionsManager;
|
||||||
|
@ -10,9 +9,4 @@ export default class ChatNotices extends Component {
|
||||||
(notice) => notice.channelId === this.args.channel.id
|
(notice) => notice.channelId === this.args.channel.id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
clearNotice(notice) {
|
|
||||||
this.subscriptionsManager.clearNotice(notice);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
{{#if this.shouldRender}}
|
|
||||||
<div class="chat-message-mention-warning alert alert-info">
|
|
||||||
{{#if this.mentionWarning.invitationSent}}
|
|
||||||
<span
|
|
||||||
class="chat-message-mention-warning__invitation-sent"
|
|
||||||
{{chat/later-fn this.onDismissInvitationSent 3000}}
|
|
||||||
>
|
|
||||||
{{d-icon "check"}}
|
|
||||||
<span>
|
|
||||||
{{i18n
|
|
||||||
"chat.mention_warning.invitations_sent"
|
|
||||||
count=this.mentionWarning.withoutMembership.length
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
class="chat-message-mention-warning__dismiss-btn btn-flat"
|
|
||||||
title={{i18n "chat.mention_warning.dismiss"}}
|
|
||||||
@action={{this.onDismissMentionWarning}}
|
|
||||||
@icon="times"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if this.mentionWarning.cannotSee}}
|
|
||||||
<p class="chat-message-mention-warning__text -cannot-see">
|
|
||||||
{{this.mentionedCannotSeeText}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.mentionWarning.withoutMembership}}
|
|
||||||
<p class="chat-message-mention-warning__text -without-membership">
|
|
||||||
<span>{{this.mentionedWithoutMembershipText}}</span>
|
|
||||||
<a href {{on "click" this.onSendInvite bubbles=false}}>
|
|
||||||
{{i18n "chat.mention_warning.invite"}}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.mentionWarning.groupWithMentionsDisabled}}
|
|
||||||
<p
|
|
||||||
class="chat-message-mention-warning__text -groups-with-mentions-disabled"
|
|
||||||
>
|
|
||||||
{{this.groupsWithDisabledMentions}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.mentionWarning.groupsWithTooManyMembers}}
|
|
||||||
<p
|
|
||||||
class="chat-message-mention-warning__text -groups-with-too-many-members"
|
|
||||||
>
|
|
||||||
{{this.groupsWithTooManyMembers}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.mentionWarning.globalMentionsDisabled}}
|
|
||||||
<p class="chat-message-mention-warning__text -global-mentions-disabled">
|
|
||||||
{{i18n "chat.mention_warning.channel_wide_mentions_disallowed"}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -1,99 +0,0 @@
|
||||||
import Component from "@glimmer/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
export default class ChatMessageMentionWarning extends Component {
|
|
||||||
@service("chat-api") api;
|
|
||||||
|
|
||||||
@action
|
|
||||||
async onSendInvite() {
|
|
||||||
const userIds = this.mentionWarning.withoutMembership.mapBy("id");
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.api.invite(this.args.message.channel.id, userIds, {
|
|
||||||
messageId: this.args.message.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.mentionWarning.invitationSent = true;
|
|
||||||
} catch (error) {
|
|
||||||
popupAjaxError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onDismissInvitationSent() {
|
|
||||||
this.mentionWarning.invitationSent = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
onDismissMentionWarning() {
|
|
||||||
this.args.message.mentionWarning = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get shouldRender() {
|
|
||||||
return (
|
|
||||||
this.mentionWarning &&
|
|
||||||
(this.mentionWarning.groupWithMentionsDisabled?.length ||
|
|
||||||
this.mentionWarning.cannotSee?.length ||
|
|
||||||
this.mentionWarning.withoutMembership?.length ||
|
|
||||||
this.mentionWarning.groupsWithTooManyMembers?.length ||
|
|
||||||
this.mentionWarning.globalMentionsDisabled)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get mentionWarning() {
|
|
||||||
return this.args.message.mentionWarning;
|
|
||||||
}
|
|
||||||
|
|
||||||
get mentionedCannotSeeText() {
|
|
||||||
return this.#findTranslatedWarning(
|
|
||||||
"chat.mention_warning.cannot_see",
|
|
||||||
"chat.mention_warning.cannot_see_multiple",
|
|
||||||
{
|
|
||||||
username: this.mentionWarning?.cannotSee?.[0]?.username,
|
|
||||||
count: this.mentionWarning?.cannotSee?.length,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get mentionedWithoutMembershipText() {
|
|
||||||
return this.#findTranslatedWarning(
|
|
||||||
"chat.mention_warning.without_membership",
|
|
||||||
"chat.mention_warning.without_membership_multiple",
|
|
||||||
{
|
|
||||||
username: this.mentionWarning?.withoutMembership?.[0]?.username,
|
|
||||||
count: this.mentionWarning?.withoutMembership?.length,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get groupsWithDisabledMentions() {
|
|
||||||
return this.#findTranslatedWarning(
|
|
||||||
"chat.mention_warning.group_mentions_disabled",
|
|
||||||
"chat.mention_warning.group_mentions_disabled_multiple",
|
|
||||||
{
|
|
||||||
group_name: this.mentionWarning?.groupWithMentionsDisabled?.[0],
|
|
||||||
count: this.mentionWarning?.groupWithMentionsDisabled?.length,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get groupsWithTooManyMembers() {
|
|
||||||
return this.#findTranslatedWarning(
|
|
||||||
"chat.mention_warning.too_many_members",
|
|
||||||
"chat.mention_warning.too_many_members_multiple",
|
|
||||||
{
|
|
||||||
group_name: this.mentionWarning.groupsWithTooManyMembers?.[0],
|
|
||||||
count: this.mentionWarning.groupsWithTooManyMembers?.length,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#findTranslatedWarning(oneKey, multipleKey, args) {
|
|
||||||
const translationKey = args.count === 1 ? oneKey : multipleKey;
|
|
||||||
args.count--;
|
|
||||||
return I18n.t(translationKey, args);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="mention-without-membership-notice">
|
||||||
|
{{#if this.invitationsSent}}
|
||||||
|
<span
|
||||||
|
class="mention-without-membership-notice__invitation-sent"
|
||||||
|
{{chat/later-fn @clearNotice 3000}}
|
||||||
|
>
|
||||||
|
{{d-icon "check"}}
|
||||||
|
<span>
|
||||||
|
{{i18n
|
||||||
|
"chat.mention_warning.invitations_sent"
|
||||||
|
count=this.userIds.length
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
<p class="mention-without-membership-notice__body -without-membership">
|
||||||
|
<span
|
||||||
|
class="mention-without-membership-notice__body__text"
|
||||||
|
>{{@notice.data.text}}</span>
|
||||||
|
<a
|
||||||
|
class="mention-without-membership-notice__body__link"
|
||||||
|
href
|
||||||
|
{{on "click" this.sendInvitations}}
|
||||||
|
>
|
||||||
|
{{i18n "chat.mention_warning.invite"}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
|
export default class MentionWithoutMembership extends Component {
|
||||||
|
@service("chat-api") chatApi;
|
||||||
|
|
||||||
|
@tracked invitationsSent = false;
|
||||||
|
|
||||||
|
get userIds() {
|
||||||
|
return this.args.notice.data.user_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async sendInvitations(event) {
|
||||||
|
// preventDefault to avoid a refresh
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.chatApi.invite(this.args.channel.id, this.userIds, {
|
||||||
|
messageId: this.args.notice.data.messageId,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.invitationsSent = true;
|
||||||
|
} catch (error) {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
<StyleguideExample @title="<Chat::Message::MentionWarning>">
|
|
||||||
<Styleguide::Component>
|
|
||||||
<Chat::Message::MentionWarning @message={{this.message}} />
|
|
||||||
</Styleguide::Component>
|
|
||||||
<Styleguide::Controls::Row @name="Cannot see">
|
|
||||||
<DToggleSwitch
|
|
||||||
@state={{gt this.message.mentionWarning.cannotSee.length 0}}
|
|
||||||
{{on "click" this.toggleCannotSee}}
|
|
||||||
/>
|
|
||||||
</Styleguide::Controls::Row>
|
|
||||||
<Styleguide::Controls::Row @name="Group with mentions disabled">
|
|
||||||
<DToggleSwitch
|
|
||||||
@state={{gt
|
|
||||||
this.message.mentionWarning.groupWithMentionsDisabled.length
|
|
||||||
0
|
|
||||||
}}
|
|
||||||
{{on "click" this.toggleGroupWithMentionsDisabled}}
|
|
||||||
/>
|
|
||||||
</Styleguide::Controls::Row>
|
|
||||||
<Styleguide::Controls::Row @name="Group with too many members">
|
|
||||||
<DToggleSwitch
|
|
||||||
@state={{gt
|
|
||||||
this.message.mentionWarning.groupsWithTooManyMembers.length
|
|
||||||
0
|
|
||||||
}}
|
|
||||||
{{on "click" this.toggleGroupsWithTooManyMembers}}
|
|
||||||
/>
|
|
||||||
</Styleguide::Controls::Row>
|
|
||||||
<Styleguide::Controls::Row @name="Without membership">
|
|
||||||
<DToggleSwitch
|
|
||||||
@state={{gt this.message.mentionWarning.withoutMembership.length 0}}
|
|
||||||
{{on "click" this.toggleWithoutMembership}}
|
|
||||||
/>
|
|
||||||
</Styleguide::Controls::Row>
|
|
||||||
</StyleguideExample>
|
|
|
@ -1,75 +0,0 @@
|
||||||
import Component from "@glimmer/component";
|
|
||||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
|
|
||||||
export default class ChatMessageMentionWarning extends Component {
|
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.message = fabricators.message({ user: this.currentUser });
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleCannotSee() {
|
|
||||||
if (this.message.mentionWarning?.cannotSee) {
|
|
||||||
this.message.mentionWarning = null;
|
|
||||||
} else {
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
cannot_see: [fabricators.user({ username: "bob" })].map((u) => {
|
|
||||||
return { username: u.username, id: u.id };
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleGroupWithMentionsDisabled() {
|
|
||||||
if (this.message.mentionWarning?.groupWithMentionsDisabled) {
|
|
||||||
this.message.mentionWarning = null;
|
|
||||||
} else {
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
group_mentions_disabled: [fabricators.group()].mapBy("name"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
toggleGroupsWithTooManyMembers() {
|
|
||||||
if (this.message.mentionWarning?.groupsWithTooManyMembers) {
|
|
||||||
this.message.mentionWarning = null;
|
|
||||||
} else {
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
groups_with_too_many_members: [
|
|
||||||
fabricators.group(),
|
|
||||||
fabricators.group({ name: "Moderators" }),
|
|
||||||
].mapBy("name"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
toggleWithoutMembership() {
|
|
||||||
if (this.message.mentionWarning?.withoutMembership) {
|
|
||||||
this.message.mentionWarning = null;
|
|
||||||
} else {
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
without_membership: [fabricators.user()].map((u) => {
|
|
||||||
return { username: u.username, id: u.id };
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,6 @@ import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||||
import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
|
import ChatThread from "discourse/plugins/chat/discourse/models/chat-thread";
|
||||||
import ChatThreadPreview from "discourse/plugins/chat/discourse/models/chat-thread-preview";
|
import ChatThreadPreview from "discourse/plugins/chat/discourse/models/chat-thread-preview";
|
||||||
import ChatDirectMessage from "discourse/plugins/chat/discourse/models/chat-direct-message";
|
import ChatDirectMessage from "discourse/plugins/chat/discourse/models/chat-direct-message";
|
||||||
import ChatMessageMentionWarning from "discourse/plugins/chat/discourse/models/chat-message-mention-warning";
|
|
||||||
import ChatMessageReaction from "discourse/plugins/chat/discourse/models/chat-message-reaction";
|
import ChatMessageReaction from "discourse/plugins/chat/discourse/models/chat-message-reaction";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import Bookmark from "discourse/models/bookmark";
|
import Bookmark from "discourse/models/bookmark";
|
||||||
|
@ -157,10 +156,6 @@ function groupFabricator(args = {}) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function messageMentionWarningFabricator(message, args = {}) {
|
|
||||||
return ChatMessageMentionWarning.create(message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFabricator() {
|
function uploadFabricator() {
|
||||||
return {
|
return {
|
||||||
extension: "jpeg",
|
extension: "jpeg",
|
||||||
|
@ -191,6 +186,5 @@ export default {
|
||||||
upload: uploadFabricator,
|
upload: uploadFabricator,
|
||||||
category: categoryFabricator,
|
category: categoryFabricator,
|
||||||
directMessage: directMessageFabricator,
|
directMessage: directMessageFabricator,
|
||||||
messageMentionWarning: messageMentionWarningFabricator,
|
|
||||||
group: groupFabricator,
|
group: groupFabricator,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
|
|
||||||
export default class ChatMessageMentionWarning {
|
|
||||||
static create(message, args = {}) {
|
|
||||||
return new ChatMessageMentionWarning(message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@tracked invitationSent = false;
|
|
||||||
@tracked cannotSee;
|
|
||||||
@tracked withoutMembership;
|
|
||||||
@tracked groupsWithTooManyMembers;
|
|
||||||
@tracked groupWithMentionsDisabled;
|
|
||||||
@tracked globalMentionsDisabled;
|
|
||||||
|
|
||||||
constructor(message, args = {}) {
|
|
||||||
this.message = args.message;
|
|
||||||
this.cannotSee = args.cannot_see;
|
|
||||||
this.withoutMembership = args.without_membership;
|
|
||||||
this.groupsWithTooManyMembers = args.groups_with_too_many_members;
|
|
||||||
this.groupWithMentionsDisabled = args.group_mentions_disabled;
|
|
||||||
this.globalMentionsDisabled = args.global_mentions_disabled;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,5 +11,7 @@ export default class ChatNotice {
|
||||||
constructor(args = {}) {
|
constructor(args = {}) {
|
||||||
this.channelId = args.channel_id;
|
this.channelId = args.channel_id;
|
||||||
this.textContent = args.text_content;
|
this.textContent = args.text_content;
|
||||||
|
this.type = args.notice_type;
|
||||||
|
this.data = args.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,11 @@ export default class ChatChannelPaneSubscriptionsManager extends ChatPaneBaseSub
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNotice(data) {
|
handleNotice(data) {
|
||||||
this.notices.push(ChatNotice.create(data));
|
this.notices.pushObject(ChatNotice.create(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
clearNotice(notice) {
|
clearNotice(notice) {
|
||||||
const index = this.notices.indexOf(notice);
|
this.notices.removeObject(notice);
|
||||||
if (index > -1) {
|
|
||||||
this.notices.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleThreadOriginalMessageUpdate(data) {
|
handleThreadOriginalMessageUpdate(data) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Service, { inject as service } from "@ember/service";
|
import Service, { inject as service } from "@ember/service";
|
||||||
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
import ChatMessage from "discourse/plugins/chat/discourse/models/chat-message";
|
||||||
import ChatMessageMentionWarning from "discourse/plugins/chat/discourse/models/chat-message-mention-warning";
|
|
||||||
import { cloneJSON } from "discourse-common/lib/object";
|
import { cloneJSON } from "discourse-common/lib/object";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
@ -105,9 +104,6 @@ export default class ChatPaneBaseSubscriptionsManager extends Service {
|
||||||
case "restore":
|
case "restore":
|
||||||
this.handleRestoreMessage(busData);
|
this.handleRestoreMessage(busData);
|
||||||
break;
|
break;
|
||||||
case "mention_warning":
|
|
||||||
this.handleMentionWarning(busData);
|
|
||||||
break;
|
|
||||||
case "self_flagged":
|
case "self_flagged":
|
||||||
this.handleSelfFlaggedMessage(busData);
|
this.handleSelfFlaggedMessage(busData);
|
||||||
break;
|
break;
|
||||||
|
@ -203,13 +199,6 @@ export default class ChatPaneBaseSubscriptionsManager extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMentionWarning(data) {
|
|
||||||
const message = this.messagesManager.findMessage(data.chat_message_id);
|
|
||||||
if (message) {
|
|
||||||
message.mentionWarning = ChatMessageMentionWarning.create(message, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSelfFlaggedMessage(data) {
|
handleSelfFlaggedMessage(data) {
|
||||||
const message = this.messagesManager.findMessage(data.chat_message_id);
|
const message = this.messagesManager.findMessage(data.chat_message_id);
|
||||||
if (message) {
|
if (message) {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
.chat-message-mention-warning {
|
|
||||||
position: relative;
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
|
|
||||||
&__dismiss-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 7px;
|
|
||||||
right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__invite-sent {
|
|
||||||
color: var(--tertiary);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -52,7 +52,6 @@
|
||||||
@import "chat-thread-list-header";
|
@import "chat-thread-list-header";
|
||||||
@import "chat-thread-unread-indicator";
|
@import "chat-thread-unread-indicator";
|
||||||
@import "chat-thread-participants";
|
@import "chat-thread-participants";
|
||||||
@import "chat-message-mention-warning";
|
|
||||||
@import "chat-message-error";
|
@import "chat-message-error";
|
||||||
@import "chat-message-creator";
|
@import "chat-message-creator";
|
||||||
@import "chat-user-avatar";
|
@import "chat-user-avatar";
|
||||||
|
|
|
@ -129,28 +129,11 @@ en:
|
||||||
one: "Last hour"
|
one: "Last hour"
|
||||||
other: "Last %{count} hours"
|
other: "Last %{count} hours"
|
||||||
mention_warning:
|
mention_warning:
|
||||||
dismiss: "dismiss"
|
|
||||||
cannot_see: "%{username} can't access this channel and was not notified."
|
|
||||||
cannot_see_multiple:
|
|
||||||
one: "%{username} and %{count} other user cannot access this channel and were not notified."
|
|
||||||
other: "%{username} and %{count} other users cannot access this channel and were not notified."
|
|
||||||
invitations_sent:
|
invitations_sent:
|
||||||
one: "Invitation sent"
|
one: "Invitation sent"
|
||||||
other: "Invitations sent"
|
other: "Invitations sent"
|
||||||
invite: "Invite to channel"
|
invite: "Invite to channel"
|
||||||
without_membership: "%{username} has not joined this channel."
|
|
||||||
without_membership_multiple:
|
|
||||||
one: "%{username} and %{count} other user have not joined this channel."
|
|
||||||
other: "%{username} and %{count} other users have not joined this channel."
|
|
||||||
group_mentions_disabled: "%{group_name} doesn't allow mentions."
|
|
||||||
group_mentions_disabled_multiple:
|
|
||||||
one: "%{group_name} and %{count} other group don't allow mentions."
|
|
||||||
other: "%{group_name} and %{count} other groups don't allow mentions."
|
|
||||||
channel_wide_mentions_disallowed: "@here and @all mentions are disabled in this channel."
|
channel_wide_mentions_disallowed: "@here and @all mentions are disabled in this channel."
|
||||||
too_many_members: "%{group_name} has too many members. No one was notified."
|
|
||||||
too_many_members_multiple:
|
|
||||||
one: "%{group_name} and %{count} other group have too many members. No one was notified."
|
|
||||||
other: "%{group_name} and %{count} other groups have too many members. No one was notified."
|
|
||||||
groups:
|
groups:
|
||||||
header:
|
header:
|
||||||
some: "Some users won't be notified"
|
some: "Some users won't be notified"
|
||||||
|
|
|
@ -138,6 +138,29 @@ en:
|
||||||
multi_user_truncated:
|
multi_user_truncated:
|
||||||
one: "%{comma_separated_usernames} and %{count} other"
|
one: "%{comma_separated_usernames} and %{count} other"
|
||||||
other: "%{comma_separated_usernames} and %{count} others"
|
other: "%{comma_separated_usernames} and %{count} others"
|
||||||
|
mention_warning:
|
||||||
|
dismiss: "dismiss"
|
||||||
|
cannot_see: "%{first_identifier} can't access this channel and was not notified."
|
||||||
|
cannot_see_multiple:
|
||||||
|
one: "%{first_identifier} and %{count} other user cannot access this channel and were not notified."
|
||||||
|
other: "%{first_identifier} and %{count} other users cannot access this channel and were not notified."
|
||||||
|
invitations_sent:
|
||||||
|
one: "Invitation sent"
|
||||||
|
other: "Invitations sent"
|
||||||
|
invite: "Invite to channel"
|
||||||
|
without_membership: "%{first_identifier} has not joined this channel."
|
||||||
|
without_membership_multiple:
|
||||||
|
one: "%{first_identifier} and %{count} other user have not joined this channel."
|
||||||
|
other: "%{first_identifier} and %{count} other users have not joined this channel."
|
||||||
|
group_mentions_disabled: "%{first_identifier} doesn't allow mentions."
|
||||||
|
group_mentions_disabled_multiple:
|
||||||
|
one: "%{first_identifier} and %{count} other group don't allow mentions."
|
||||||
|
other: "%{first_identifier} and %{count} other groups don't allow mentions."
|
||||||
|
global_mentions_disallowed: "@here and @all mentions are disabled in this channel."
|
||||||
|
too_many_members: "%{first_identifier} has too many members. No one was notified."
|
||||||
|
too_many_members_multiple:
|
||||||
|
one: "%{first_identifier} and %{count} other group have too many members. No one was notified."
|
||||||
|
other: "%{first_identifier} and %{count} other groups have too many members. No one was notified."
|
||||||
|
|
||||||
category_channel:
|
category_channel:
|
||||||
errors:
|
errors:
|
||||||
|
|
|
@ -225,24 +225,100 @@ module Chat
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_creator_of_inaccessible_mentions(inaccessible)
|
def notify_creator_of_inaccessible_mentions(inaccessible)
|
||||||
group_mentions_disabled = @parsed_mentions.groups_with_disabled_mentions.to_a
|
# Notify when mentioned users can join channel, but don't have a membership
|
||||||
too_many_members = @parsed_mentions.groups_with_too_many_members.to_a
|
if inaccessible[:welcome_to_join].any?
|
||||||
if inaccessible.values.all?(&:blank?) && group_mentions_disabled.empty? &&
|
publish_inaccessible_mentions(inaccessible[:welcome_to_join])
|
||||||
too_many_members.empty? && !global_mentions_disabled
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Chat::Publisher.publish_inaccessible_mentions(
|
# Notify when mentioned users are not able to access the channel
|
||||||
@user.id,
|
publish_unreachable_mentions(inaccessible[:unreachable]) if inaccessible[:unreachable].any?
|
||||||
@chat_message,
|
|
||||||
inaccessible[:unreachable].to_a,
|
# Notify when `@all` or `@here` is used when channel has global mentions disabled
|
||||||
inaccessible[:welcome_to_join].to_a,
|
publish_global_mentions_disabled if global_mentions_disabled
|
||||||
too_many_members,
|
|
||||||
group_mentions_disabled,
|
# Notify when groups are mentioned and have mentions disabled
|
||||||
global_mentions_disabled,
|
group_mentions_disabled = @parsed_mentions.groups_with_disabled_mentions.to_a
|
||||||
|
publish_group_mentions_disabled(group_mentions_disabled) if group_mentions_disabled.any?
|
||||||
|
|
||||||
|
# Notify when large groups are mentioned, exceeding `max_users_notified_per_group_mention`
|
||||||
|
too_many_members = @parsed_mentions.groups_with_too_many_members.to_a
|
||||||
|
publish_too_many_members_in_group_mention(too_many_members) if too_many_members.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def publish_inaccessible_mentions(users)
|
||||||
|
Chat::Publisher.publish_notice(
|
||||||
|
user_id: @user.id,
|
||||||
|
channel_id: @chat_channel.id,
|
||||||
|
type: "mention_without_membership",
|
||||||
|
data: {
|
||||||
|
user_ids: users.map(&:id),
|
||||||
|
text:
|
||||||
|
mention_warning_text(
|
||||||
|
single: "chat.mention_warning.without_membership",
|
||||||
|
multiple: "chat.mention_warning.without_membership_multiple",
|
||||||
|
first_identifier: users.first.username,
|
||||||
|
count: users.count,
|
||||||
|
),
|
||||||
|
message_id: @chat_message.id,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def publish_group_mentions_disabled(groups)
|
||||||
|
Chat::Publisher.publish_notice(
|
||||||
|
user_id: @user.id,
|
||||||
|
channel_id: @chat_channel.id,
|
||||||
|
text_content:
|
||||||
|
mention_warning_text(
|
||||||
|
single: "chat.mention_warning.group_mentions_disabled",
|
||||||
|
multiple: "chat.mention_warning.group_mentions_disabled_multiple",
|
||||||
|
first_identifier: groups.first.name,
|
||||||
|
count: groups.count,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def publish_global_mentions_disabled
|
||||||
|
Chat::Publisher.publish_notice(
|
||||||
|
user_id: @user.id,
|
||||||
|
channel_id: @chat_channel.id,
|
||||||
|
text_content: I18n.t("chat.mention_warning.global_mentions_disallowed"),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def publish_unreachable_mentions(users)
|
||||||
|
Chat::Publisher.publish_notice(
|
||||||
|
user_id: @user.id,
|
||||||
|
channel_id: @chat_channel.id,
|
||||||
|
text_content:
|
||||||
|
mention_warning_text(
|
||||||
|
single: "chat.mention_warning.cannot_see",
|
||||||
|
multiple: "chat.mention_warning.cannot_see_multiple",
|
||||||
|
first_identifier: users.first.username,
|
||||||
|
count: users.count,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def publish_too_many_members_in_group_mention(groups)
|
||||||
|
Chat::Publisher.publish_notice(
|
||||||
|
user_id: @user.id,
|
||||||
|
channel_id: @chat_channel.id,
|
||||||
|
text_content:
|
||||||
|
mention_warning_text(
|
||||||
|
single: "chat.mention_warning.too_many_members",
|
||||||
|
multiple: "chat.mention_warning.too_many_members_multiple",
|
||||||
|
first_identifier: groups.first.name,
|
||||||
|
count: groups.count,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mention_warning_text(single:, multiple:, first_identifier:, count:)
|
||||||
|
translation_key = count == 1 ? single : multiple
|
||||||
|
I18n.t(translation_key, first_identifier: first_identifier, count: count - 1)
|
||||||
|
end
|
||||||
|
|
||||||
def global_mentions_disabled
|
def global_mentions_disabled
|
||||||
return @global_mentions_disabled if defined?(@global_mentions_disabled)
|
return @global_mentions_disabled if defined?(@global_mentions_disabled)
|
||||||
|
|
||||||
|
|
|
@ -488,7 +488,7 @@ describe Chat::MessageCreator do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "publishes inaccessible mentions when user isn't aren't a part of the channel" do
|
it "publishes inaccessible mentions when user isn't aren't a part of the channel" do
|
||||||
Chat::Publisher.expects(:publish_inaccessible_mentions).once
|
Chat::Publisher.expects(:publish_notice).once
|
||||||
described_class.create(
|
described_class.create(
|
||||||
chat_channel: public_chat_channel,
|
chat_channel: public_chat_channel,
|
||||||
user: admin1,
|
user: admin1,
|
||||||
|
@ -498,7 +498,7 @@ describe Chat::MessageCreator do
|
||||||
|
|
||||||
it "publishes inaccessible mentions when user doesn't have chat access" do
|
it "publishes inaccessible mentions when user doesn't have chat access" do
|
||||||
SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff]
|
SiteSetting.chat_allowed_groups = Group::AUTO_GROUPS[:staff]
|
||||||
Chat::Publisher.expects(:publish_inaccessible_mentions).once
|
Chat::Publisher.expects(:publish_notice).once
|
||||||
described_class.create(
|
described_class.create(
|
||||||
chat_channel: public_chat_channel,
|
chat_channel: public_chat_channel,
|
||||||
user: admin1,
|
user: admin1,
|
||||||
|
@ -507,7 +507,7 @@ describe Chat::MessageCreator do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't publish inaccessible mentions when user is following channel" do
|
it "doesn't publish inaccessible mentions when user is following channel" do
|
||||||
Chat::Publisher.expects(:publish_inaccessible_mentions).never
|
Chat::Publisher.expects(:publish_notice).never
|
||||||
described_class.create(
|
described_class.create(
|
||||||
chat_channel: public_chat_channel,
|
chat_channel: public_chat_channel,
|
||||||
user: admin1,
|
user: admin1,
|
||||||
|
|
|
@ -69,9 +69,10 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
global_mentions_disabled_message = messages.first
|
global_mentions_disabled_message = messages.first
|
||||||
|
|
||||||
expect(global_mentions_disabled_message).to be_present
|
expect(global_mentions_disabled_message.data[:type].to_sym).to eq(:notice)
|
||||||
expect(global_mentions_disabled_message.data[:type].to_sym).to eq(:mention_warning)
|
expect(global_mentions_disabled_message.data[:text_content]).to eq(
|
||||||
expect(global_mentions_disabled_message.data[:global_mentions_disabled]).to eq(true)
|
I18n.t("chat.mention_warning.global_mentions_disallowed"),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "includes all members of a channel except the sender" do
|
it "includes all members of a channel except the sender" do
|
||||||
|
@ -415,10 +416,10 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
unreachable_msg = messages.first
|
unreachable_msg = messages.first
|
||||||
|
|
||||||
expect(unreachable_msg).to be_present
|
expect(unreachable_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(unreachable_msg.data[:without_membership]).to be_empty
|
expect(unreachable_msg[:data][:text_content]).to eq(
|
||||||
unreachable_users = unreachable_msg.data[:cannot_see].map { |u| u["id"] }
|
I18n.t("chat.mention_warning.cannot_see", first_identifier: user_3.username),
|
||||||
expect(unreachable_users).to contain_exactly(user_3.id)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when in a personal message" do
|
context "when in a personal message" do
|
||||||
|
@ -452,10 +453,10 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
unreachable_msg = messages.first
|
unreachable_msg = messages.first
|
||||||
|
|
||||||
expect(unreachable_msg).to be_present
|
expect(unreachable_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(unreachable_msg.data[:without_membership]).to be_empty
|
expect(unreachable_msg[:data][:text_content]).to eq(
|
||||||
unreachable_users = unreachable_msg.data[:cannot_see].map { |u| u["id"] }
|
I18n.t("chat.mention_warning.cannot_see", first_identifier: user_3.username),
|
||||||
expect(unreachable_users).to contain_exactly(user_3.id)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "notify posts of users who are part of the mentioned group but participating" do
|
it "notify posts of users who are part of the mentioned group but participating" do
|
||||||
|
@ -477,10 +478,10 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
unreachable_msg = messages.first
|
unreachable_msg = messages.first
|
||||||
|
|
||||||
expect(unreachable_msg).to be_present
|
expect(unreachable_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(unreachable_msg.data[:without_membership]).to be_empty
|
expect(unreachable_msg[:data][:text_content]).to eq(
|
||||||
unreachable_users = unreachable_msg.data[:cannot_see].map { |u| u["id"] }
|
I18n.t("chat.mention_warning.cannot_see", first_identifier: user_3.username),
|
||||||
expect(unreachable_users).to contain_exactly(user_3.id)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -502,11 +503,15 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
not_participating_msg = messages.first
|
not_participating_msg = messages.first
|
||||||
|
|
||||||
expect(not_participating_msg).to be_present
|
expect(not_participating_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(not_participating_msg.data[:cannot_see]).to be_empty
|
expect(not_participating_msg[:data][:text_content]).to be_nil
|
||||||
not_participating_users =
|
expect(not_participating_msg[:data][:notice_type].to_sym).to eq(:mention_without_membership)
|
||||||
not_participating_msg.data[:without_membership].map { |u| u["id"] }
|
expect(not_participating_msg[:data][:data]).to eq(
|
||||||
expect(not_participating_users).to contain_exactly(user_3.id)
|
user_ids: [user_3.id],
|
||||||
|
text:
|
||||||
|
I18n.t("chat.mention_warning.without_membership", first_identifier: user_3.username),
|
||||||
|
message_id: msg.id,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "cannot invite chat user without channel membership if they are ignoring the user who created the message" do
|
it "cannot invite chat user without channel membership if they are ignoring the user who created the message" do
|
||||||
|
@ -555,11 +560,15 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
not_participating_msg = messages.first
|
not_participating_msg = messages.first
|
||||||
|
|
||||||
expect(not_participating_msg).to be_present
|
expect(not_participating_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(not_participating_msg.data[:cannot_see]).to be_empty
|
expect(not_participating_msg[:data][:text_content]).to be_nil
|
||||||
not_participating_users =
|
expect(not_participating_msg[:data][:notice_type].to_sym).to eq(:mention_without_membership)
|
||||||
not_participating_msg.data[:without_membership].map { |u| u["id"] }
|
expect(not_participating_msg[:data][:data]).to eq(
|
||||||
expect(not_participating_users).to contain_exactly(user_3.id)
|
user_ids: [user_3.id],
|
||||||
|
text:
|
||||||
|
I18n.t("chat.mention_warning.without_membership", first_identifier: user_3.username),
|
||||||
|
message_id: msg.id,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can invite other group members to channel" do
|
it "can invite other group members to channel" do
|
||||||
|
@ -580,11 +589,15 @@ describe Chat::Notifier do
|
||||||
|
|
||||||
not_participating_msg = messages.first
|
not_participating_msg = messages.first
|
||||||
|
|
||||||
expect(not_participating_msg).to be_present
|
expect(not_participating_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(not_participating_msg.data[:cannot_see]).to be_empty
|
expect(not_participating_msg[:data][:text_content]).to be_nil
|
||||||
not_participating_users =
|
expect(not_participating_msg[:data][:notice_type].to_sym).to eq(:mention_without_membership)
|
||||||
not_participating_msg.data[:without_membership].map { |u| u["id"] }
|
expect(not_participating_msg[:data][:data]).to eq(
|
||||||
expect(not_participating_users).to contain_exactly(user_3.id)
|
user_ids: [user_3.id],
|
||||||
|
text:
|
||||||
|
I18n.t("chat.mention_warning.without_membership", first_identifier: user_3.username),
|
||||||
|
message_id: msg.id,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "cannot invite a member of a group who is ignoring the user who created the message" do
|
it "cannot invite a member of a group who is ignoring the user who created the message" do
|
||||||
|
@ -650,9 +663,11 @@ describe Chat::Notifier do
|
||||||
end
|
end
|
||||||
|
|
||||||
too_many_members_msg = messages.first
|
too_many_members_msg = messages.first
|
||||||
expect(too_many_members_msg).to be_present
|
|
||||||
too_many_members = too_many_members_msg.data[:groups_with_too_many_members]
|
expect(too_many_members_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(too_many_members).to contain_exactly(group.name)
|
expect(too_many_members_msg[:data][:text_content]).to eq(
|
||||||
|
I18n.t("chat.mention_warning.too_many_members", first_identifier: group.name),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sends a message to the client signaling the group doesn't allow mentions" do
|
it "sends a message to the client signaling the group doesn't allow mentions" do
|
||||||
|
@ -667,9 +682,11 @@ describe Chat::Notifier do
|
||||||
end
|
end
|
||||||
|
|
||||||
mentions_disabled_msg = messages.first
|
mentions_disabled_msg = messages.first
|
||||||
expect(mentions_disabled_msg).to be_present
|
|
||||||
mentions_disabled = mentions_disabled_msg.data[:group_mentions_disabled]
|
expect(mentions_disabled_msg[:data][:type].to_sym).to eq(:notice)
|
||||||
expect(mentions_disabled).to contain_exactly(group.name)
|
expect(mentions_disabled_msg[:data][:text_content]).to eq(
|
||||||
|
I18n.t("chat.mention_warning.group_mentions_disabled", first_identifier: group.name),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
RSpec.describe "JIT messages", type: :system do
|
|
||||||
fab!(:channel_1) { Fabricate(:chat_channel) }
|
|
||||||
fab!(:current_user) { Fabricate(:user) }
|
|
||||||
fab!(:other_user) { Fabricate(:user) }
|
|
||||||
|
|
||||||
let(:chat) { PageObjects::Pages::Chat.new }
|
|
||||||
let(:channel) { PageObjects::Pages::ChatChannel.new }
|
|
||||||
|
|
||||||
before do
|
|
||||||
channel_1.add(current_user)
|
|
||||||
chat_system_bootstrap
|
|
||||||
sign_in(current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when mentioning a user" do
|
|
||||||
context "when user is not on the channel" do
|
|
||||||
it "displays a mention warning" do
|
|
||||||
Jobs.run_immediately!
|
|
||||||
|
|
||||||
chat.visit_channel(channel_1)
|
|
||||||
channel.send_message("hi @#{other_user.username}")
|
|
||||||
|
|
||||||
expect(page).to have_content(
|
|
||||||
I18n.t("js.chat.mention_warning.without_membership", username: other_user.username),
|
|
||||||
wait: 5,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when user can’t access the channel" do
|
|
||||||
fab!(:group_1) { Fabricate(:group) }
|
|
||||||
fab!(:private_channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group_1.add(current_user)
|
|
||||||
private_channel_1.add(current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "displays a mention warning" do
|
|
||||||
Jobs.run_immediately!
|
|
||||||
|
|
||||||
chat.visit_channel(private_channel_1)
|
|
||||||
channel.send_message("hi @#{other_user.username}")
|
|
||||||
|
|
||||||
expect(page).to have_content(
|
|
||||||
I18n.t("js.chat.mention_warning.cannot_see", username: other_user.username),
|
|
||||||
wait: 5,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when category channel permission is readonly for everyone" do
|
|
||||||
fab!(:group_1) { Fabricate(:group) }
|
|
||||||
fab!(:private_channel_1) { Fabricate(:private_category_channel, group: group_1) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group_1.add(current_user)
|
|
||||||
private_channel_1.add(current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "displays a mention warning" do
|
|
||||||
Jobs.run_immediately!
|
|
||||||
|
|
||||||
chat.visit_channel(private_channel_1)
|
|
||||||
channel.send_message("hi @#{other_user.username}")
|
|
||||||
|
|
||||||
expect(page).to have_content(
|
|
||||||
I18n.t("js.chat.mention_warning.cannot_see", username: other_user.username),
|
|
||||||
wait: 5,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when mention a group" do
|
|
||||||
context "when group can't be mentioned" do
|
|
||||||
fab!(:group_1) { Fabricate(:group, mentionable_level: Group::ALIAS_LEVELS[:nobody]) }
|
|
||||||
|
|
||||||
it "displays a mention warning" do
|
|
||||||
Jobs.run_immediately!
|
|
||||||
|
|
||||||
chat.visit_channel(channel_1)
|
|
||||||
channel.send_message("hi @#{group_1.name}")
|
|
||||||
|
|
||||||
expect(page).to have_content(
|
|
||||||
I18n.t("js.chat.mention_warning.group_mentions_disabled", group_name: group_1.name),
|
|
||||||
wait: 5,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,118 +0,0 @@
|
||||||
import { render } from "@ember/test-helpers";
|
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
||||||
import hbs from "htmlbars-inline-precompile";
|
|
||||||
import { module, test } from "qunit";
|
|
||||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
|
||||||
|
|
||||||
module(
|
|
||||||
"Discourse Chat | Component | Chat::Message::MentionWarning",
|
|
||||||
function (hooks) {
|
|
||||||
setupRenderingTest(hooks);
|
|
||||||
|
|
||||||
const template = hbs`
|
|
||||||
<Chat::Message::MentionWarning @message={{this.message}} />
|
|
||||||
`;
|
|
||||||
|
|
||||||
test("without memberships", async function (assert) {
|
|
||||||
this.message = fabricators.message();
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
without_membership: [fabricators.user()].map((u) => {
|
|
||||||
return { username: u.username, id: u.id };
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(template);
|
|
||||||
|
|
||||||
assert
|
|
||||||
.dom(".chat-message-mention-warning__text.-without-membership")
|
|
||||||
.exists();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("cannot see channel", async function (assert) {
|
|
||||||
this.message = fabricators.message();
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
cannot_see: [fabricators.user()].map((u) => {
|
|
||||||
return { username: u.username, id: u.id };
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(template);
|
|
||||||
|
|
||||||
assert.dom(".chat-message-mention-warning__text.-cannot-see").exists();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("cannot see channel", async function (assert) {
|
|
||||||
this.message = fabricators.message();
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
cannot_see: [fabricators.user()].map((u) => {
|
|
||||||
return { username: u.username, id: u.id };
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(template);
|
|
||||||
|
|
||||||
assert.dom(".chat-message-mention-warning__text.-cannot-see").exists();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("too many groups", async function (assert) {
|
|
||||||
this.message = fabricators.message();
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
groups_with_too_many_members: [fabricators.group()].mapBy("name"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(template);
|
|
||||||
|
|
||||||
assert
|
|
||||||
.dom(
|
|
||||||
".chat-message-mention-warning__text.-groups-with-too-many-members"
|
|
||||||
)
|
|
||||||
.exists();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("groups with mentions disabled", async function (assert) {
|
|
||||||
this.message = fabricators.message();
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
group_mentions_disabled: [fabricators.group()].mapBy("name"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(template);
|
|
||||||
|
|
||||||
assert
|
|
||||||
.dom(
|
|
||||||
".chat-message-mention-warning__text.-groups-with-mentions-disabled"
|
|
||||||
)
|
|
||||||
.exists();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("displays a warning when global mentions are disabled", async function (assert) {
|
|
||||||
this.message = fabricators.message();
|
|
||||||
this.message.mentionWarning = fabricators.messageMentionWarning(
|
|
||||||
this.message,
|
|
||||||
{
|
|
||||||
global_mentions_disabled: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await render(template);
|
|
||||||
|
|
||||||
assert
|
|
||||||
.dom(".chat-message-mention-warning__text.-global-mentions-disabled")
|
|
||||||
.exists();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import I18n from "I18n";
|
||||||
|
import pretender from "discourse/tests/helpers/create-pretender";
|
||||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||||
import hbs from "htmlbars-inline-precompile";
|
import hbs from "htmlbars-inline-precompile";
|
||||||
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||||
|
@ -62,4 +64,52 @@ module("Discourse Chat | Component | chat-notice", function (hooks) {
|
||||||
"Notice was cleared"
|
"Notice was cleared"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
test("MentionWithoutMembership notice renders", async function (assert) {
|
||||||
|
this.channel = fabricators.channel();
|
||||||
|
this.manager = this.container.lookup(
|
||||||
|
"service:chatChannelPaneSubscriptionsManager"
|
||||||
|
);
|
||||||
|
const text = "Joffrey can't chat, hermano";
|
||||||
|
this.manager.handleNotice({
|
||||||
|
channel_id: this.channel.id,
|
||||||
|
notice_type: "mention_without_membership",
|
||||||
|
data: { user_ids: [1], message_id: 1, text },
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(hbs`<ChatNotices @channel={{this.channel}} />`);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
queryAll(
|
||||||
|
".chat-notices .chat-notices__notice .mention-without-membership-notice"
|
||||||
|
).length,
|
||||||
|
1,
|
||||||
|
"Notice is present"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.dom(".mention-without-membership-notice__body__text").hasText(text);
|
||||||
|
assert
|
||||||
|
.dom(".mention-without-membership-notice__body__link")
|
||||||
|
.hasText(I18n.t("chat.mention_warning.invite"));
|
||||||
|
|
||||||
|
pretender.put(`/chat/${this.channel.id}/invite`, () => {
|
||||||
|
return [200, { "Content-Type": "application/json" }, {}];
|
||||||
|
});
|
||||||
|
|
||||||
|
await click(
|
||||||
|
query(".mention-without-membership-notice__body__link"),
|
||||||
|
"Invites the user"
|
||||||
|
);
|
||||||
|
|
||||||
|
// I would love to test that the invitation sent text is present here but
|
||||||
|
// dismiss is called right away instead of waiting 3 seconds.. Not much we can
|
||||||
|
// do about this - at least we are testing that nothing broke all the way through
|
||||||
|
// clearing the notice
|
||||||
|
assert.strictEqual(
|
||||||
|
queryAll(
|
||||||
|
".chat-notices .chat-notices__notice .mention-without-membership-notice"
|
||||||
|
).length,
|
||||||
|
0,
|
||||||
|
"Notice has been cleared"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue