mirror of
https://github.com/discourse/discourse.git
synced 2025-02-07 08:24:00 +08:00
DEV: Introduce Chat Notices with publishing method (#22369)
This commit is contained in:
parent
c6cd3af5b5
commit
3171fd1a0a
|
@ -475,6 +475,12 @@ module Chat
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.publish_notice(user_id:, channel_id:, text_content:)
|
||||||
|
payload = { type: "notice", text_content: text_content, channel_id: channel_id }
|
||||||
|
|
||||||
|
MessageBus.publish("/chat/#{channel_id}", payload, user_ids: [user_id])
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.permissions(chat_channel)
|
def self.permissions(chat_channel)
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
@displayed={{this.includeHeader}}
|
@displayed={{this.includeHeader}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ChatRetentionReminder @channel={{@channel}} />
|
<Chat::Notices @channel={{@channel}} />
|
||||||
|
|
||||||
<ChatMentionWarnings />
|
<ChatMentionWarnings />
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="chat-notices">
|
||||||
|
<ChatRetentionReminder @channel={{@channel}} />
|
||||||
|
|
||||||
|
{{#each this.noticesForChannel as |notice|}}
|
||||||
|
<div class="chat-notices__notice">
|
||||||
|
<p class="chat-notices__notice__content">
|
||||||
|
{{notice.textContent}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@icon="times"
|
||||||
|
@class="btn-flat chat-notices__notice__clear"
|
||||||
|
@action={{fn this.clearNotice notice}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
|
@ -0,0 +1,18 @@
|
||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
|
||||||
|
export default class ChatNotices extends Component {
|
||||||
|
@service("chat-channel-pane-subscriptions-manager") subscriptionsManager;
|
||||||
|
|
||||||
|
get noticesForChannel() {
|
||||||
|
return this.subscriptionsManager.notices.filter(
|
||||||
|
(notice) => notice.channelId === this.args.channel.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
clearNotice(notice) {
|
||||||
|
this.subscriptionsManager.clearNotice(notice);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
|
||||||
|
export default class ChatNotice {
|
||||||
|
static create(args = {}) {
|
||||||
|
return new ChatNotice(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@tracked channelId;
|
||||||
|
@tracked textContent;
|
||||||
|
|
||||||
|
constructor(args = {}) {
|
||||||
|
this.channelId = args.channel_id;
|
||||||
|
this.textContent = args.text_content;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,16 @@
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
||||||
import ChatPaneBaseSubscriptionsManager from "./chat-pane-base-subscriptions-manager";
|
import ChatPaneBaseSubscriptionsManager from "./chat-pane-base-subscriptions-manager";
|
||||||
import ChatThreadPreview from "../models/chat-thread-preview";
|
import ChatThreadPreview from "../models/chat-thread-preview";
|
||||||
|
import ChatNotice from "../models/chat-notice";
|
||||||
|
|
||||||
export default class ChatChannelPaneSubscriptionsManager extends ChatPaneBaseSubscriptionsManager {
|
export default class ChatChannelPaneSubscriptionsManager extends ChatPaneBaseSubscriptionsManager {
|
||||||
@service chat;
|
@service chat;
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
|
|
||||||
|
@tracked notices = new TrackedArray();
|
||||||
|
|
||||||
get messageBusChannel() {
|
get messageBusChannel() {
|
||||||
return `/chat/${this.model.id}`;
|
return `/chat/${this.model.id}`;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +23,17 @@ export default class ChatChannelPaneSubscriptionsManager extends ChatPaneBaseSub
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNotice(data) {
|
||||||
|
this.notices.push(ChatNotice.create(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
clearNotice(notice) {
|
||||||
|
const index = this.notices.indexOf(notice);
|
||||||
|
if (index > -1) {
|
||||||
|
this.notices.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleThreadOriginalMessageUpdate(data) {
|
handleThreadOriginalMessageUpdate(data) {
|
||||||
const message = this.messagesManager.findMessage(data.original_message_id);
|
const message = this.messagesManager.findMessage(data.original_message_id);
|
||||||
if (message) {
|
if (message) {
|
||||||
|
|
|
@ -116,6 +116,9 @@ export default class ChatPaneBaseSubscriptionsManager extends Service {
|
||||||
case "update_thread_original_message":
|
case "update_thread_original_message":
|
||||||
this.handleThreadOriginalMessageUpdate(busData);
|
this.handleThreadOriginalMessageUpdate(busData);
|
||||||
break;
|
break;
|
||||||
|
case "notice":
|
||||||
|
this.handleNotice(busData);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +251,10 @@ export default class ChatPaneBaseSubscriptionsManager extends Service {
|
||||||
throw "not implemented";
|
throw "not implemented";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNotice() {
|
||||||
|
throw "not implemented";
|
||||||
|
}
|
||||||
|
|
||||||
_afterDeleteMessage() {
|
_afterDeleteMessage() {
|
||||||
throw "not implemented";
|
throw "not implemented";
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,11 @@ export default class ChatThreadPaneSubscriptionsManager extends ChatPaneBaseSubs
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: We don't yet handle notices inside of threads so do nothing.
|
||||||
|
handleNotice() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_afterDeleteMessage(targetMsg, data) {
|
_afterDeleteMessage(targetMsg, data) {
|
||||||
if (this.model.currentUserMembership?.lastReadMessageId === targetMsg.id) {
|
if (this.model.currentUserMembership?.lastReadMessageId === targetMsg.id) {
|
||||||
this.model.currentUserMembership.lastReadMessageId =
|
this.model.currentUserMembership.lastReadMessageId =
|
||||||
|
|
42
plugins/chat/assets/stylesheets/common/chat-notices.scss
Normal file
42
plugins/chat/assets/stylesheets/common/chat-notices.scss
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
.chat-notices {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
min-width: 280px;
|
||||||
|
|
||||||
|
&__notice,
|
||||||
|
.chat-retention-reminder {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: var(--tertiary-low);
|
||||||
|
padding: 0.5em 0 0.5em 1em;
|
||||||
|
color: var(--primary);
|
||||||
|
padding: 0.5em 0 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-flat {
|
||||||
|
margin: 0 0.25em;
|
||||||
|
color: var(--primary-medium);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: transparent;
|
||||||
|
.d-icon {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.d-icon {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-page-chat .chat-notices {
|
||||||
|
top: 4rem;
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
.chat-retention-reminder {
|
|
||||||
display: flex;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
background: var(--tertiary-low);
|
|
||||||
padding: 0.5em 0 0.5em 1em;
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
color: var(--primary);
|
|
||||||
z-index: 10;
|
|
||||||
min-width: 280px;
|
|
||||||
|
|
||||||
.btn-flat.dismiss-btn {
|
|
||||||
margin-left: 0.25em;
|
|
||||||
color: var(--primary-medium);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: transparent;
|
|
||||||
.d-icon {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.d-icon {
|
|
||||||
color: var(--primary-medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-page-chat .chat-retention-reminder {
|
|
||||||
top: 4rem;
|
|
||||||
}
|
|
|
@ -28,10 +28,10 @@
|
||||||
@import "chat-message-separator";
|
@import "chat-message-separator";
|
||||||
@import "chat-message-thread-indicator";
|
@import "chat-message-thread-indicator";
|
||||||
@import "chat-message";
|
@import "chat-message";
|
||||||
|
@import "chat-notices";
|
||||||
@import "chat-onebox";
|
@import "chat-onebox";
|
||||||
@import "chat-reply";
|
@import "chat-reply";
|
||||||
@import "chat-replying-indicator";
|
@import "chat-replying-indicator";
|
||||||
@import "chat-retention-reminder";
|
|
||||||
@import "chat-selection-manager";
|
@import "chat-selection-manager";
|
||||||
@import "chat-side-panel";
|
@import "chat-side-panel";
|
||||||
@import "chat-skeleton";
|
@import "chat-skeleton";
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||||
|
import hbs from "htmlbars-inline-precompile";
|
||||||
|
import fabricators from "discourse/plugins/chat/discourse/lib/fabricators";
|
||||||
|
import { query, queryAll } from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import { module, test } from "qunit";
|
||||||
|
import { click, render } from "@ember/test-helpers";
|
||||||
|
|
||||||
|
module("Discourse Chat | Component | chat-notice", function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
test("displays all notices for a channel", async function (assert) {
|
||||||
|
this.channel = fabricators.channel();
|
||||||
|
this.manager = this.container.lookup(
|
||||||
|
"service:chatChannelPaneSubscriptionsManager"
|
||||||
|
);
|
||||||
|
this.manager.handleNotice({
|
||||||
|
channel_id: this.channel.id,
|
||||||
|
text_content: "hello",
|
||||||
|
});
|
||||||
|
this.manager.handleNotice({
|
||||||
|
channel_id: this.channel.id,
|
||||||
|
text_content: "goodbye",
|
||||||
|
});
|
||||||
|
this.manager.handleNotice({
|
||||||
|
channel_id: this.channel.id + 1,
|
||||||
|
text_content: "N/A",
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(hbs`<ChatNotices @channel={{this.channel}} />`);
|
||||||
|
|
||||||
|
const notices = queryAll(".chat-notices .chat-notices__notice");
|
||||||
|
|
||||||
|
assert.strictEqual(notices.length, 2, "Two notices are rendered");
|
||||||
|
|
||||||
|
assert.true(notices[0].innerText.includes("hello"));
|
||||||
|
assert.true(notices[1].innerText.includes("goodbye"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Notices can be cleared", async function (assert) {
|
||||||
|
this.channel = fabricators.channel();
|
||||||
|
this.manager = this.container.lookup(
|
||||||
|
"service:chatChannelPaneSubscriptionsManager"
|
||||||
|
);
|
||||||
|
this.manager.handleNotice({
|
||||||
|
channel_id: this.channel.id,
|
||||||
|
text_content: "hello",
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(hbs`<ChatNotices @channel={{this.channel}} />`);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
queryAll(".chat-notices .chat-notices__notice").length,
|
||||||
|
1,
|
||||||
|
"Notice is present"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(query(".chat-notices__notice__clear"), "Clear the notice");
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
queryAll(".chat-notices .chat-notices__notice").length,
|
||||||
|
0,
|
||||||
|
"Notice was cleared"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user