mirror of
https://github.com/discourse/discourse.git
synced 2025-04-08 11:10:47 +08:00
DEV: allows a context when creating a message (#25647)
The service `Chat::CreateMessage` will now accept `context_post_ids` and `context_topic_id` as params. These values represent the topic which might be visible when sending a message (for now, this is only possible when using the drawer). The `DiscourseEvent` `chat_message_created` will now have the following signature: ```ruby on(:chat_message_created) do | message, channel, user, meta| p meta[:context][:post_ids] end ```
This commit is contained in:
parent
2bd0a8f432
commit
06bbed69f9
@ -16,6 +16,8 @@ module Chat
|
|||||||
# @param in_reply_to_id [Integer] ID of a message to reply to
|
# @param in_reply_to_id [Integer] ID of a message to reply to
|
||||||
# @param thread_id [Integer] ID of a thread to reply to
|
# @param thread_id [Integer] ID of a thread to reply to
|
||||||
# @param upload_ids [Array<Integer>] IDs of uploaded documents
|
# @param upload_ids [Array<Integer>] IDs of uploaded documents
|
||||||
|
# @param context_topic_id [Integer] ID of the currently visible topic in drawer mode
|
||||||
|
# @param context_post_ids [Array<Integer>] IDs of the currently visible posts in drawer mode
|
||||||
# @param staged_id [String] arbitrary string that will be sent back to the client
|
# @param staged_id [String] arbitrary string that will be sent back to the client
|
||||||
# @param incoming_chat_webhook [Chat::IncomingWebhook]
|
# @param incoming_chat_webhook [Chat::IncomingWebhook]
|
||||||
|
|
||||||
@ -50,6 +52,8 @@ module Chat
|
|||||||
class Contract
|
class Contract
|
||||||
attribute :chat_channel_id, :string
|
attribute :chat_channel_id, :string
|
||||||
attribute :in_reply_to_id, :string
|
attribute :in_reply_to_id, :string
|
||||||
|
attribute :context_topic_id, :integer
|
||||||
|
attribute :context_post_ids, :array
|
||||||
attribute :message, :string
|
attribute :message, :string
|
||||||
attribute :staged_id, :string
|
attribute :staged_id, :string
|
||||||
attribute :upload_ids, :array
|
attribute :upload_ids, :array
|
||||||
@ -185,11 +189,13 @@ module Chat
|
|||||||
|
|
||||||
def process(channel:, message_instance:, contract:, **)
|
def process(channel:, message_instance:, contract:, **)
|
||||||
::Chat::Publisher.publish_new!(channel, message_instance, contract.staged_id)
|
::Chat::Publisher.publish_new!(channel, message_instance, contract.staged_id)
|
||||||
|
|
||||||
DiscourseEvent.trigger(
|
DiscourseEvent.trigger(
|
||||||
:chat_message_created,
|
:chat_message_created,
|
||||||
message_instance,
|
message_instance,
|
||||||
channel,
|
channel,
|
||||||
message_instance.user,
|
message_instance.user,
|
||||||
|
{ context: { post_ids: contract.context_post_ids, topic_id: contract.context_topic_id } },
|
||||||
)
|
)
|
||||||
|
|
||||||
if contract.process_inline
|
if contract.process_inline
|
||||||
|
@ -35,6 +35,7 @@ import {
|
|||||||
checkMessageTopVisibility,
|
checkMessageTopVisibility,
|
||||||
} from "discourse/plugins/chat/discourse/lib/check-message-visibility";
|
} from "discourse/plugins/chat/discourse/lib/check-message-visibility";
|
||||||
import DatesSeparatorsPositioner from "discourse/plugins/chat/discourse/lib/dates-separators-positioner";
|
import DatesSeparatorsPositioner from "discourse/plugins/chat/discourse/lib/dates-separators-positioner";
|
||||||
|
import { extractCurrentTopicInfo } from "discourse/plugins/chat/discourse/lib/extract-current-topic-info";
|
||||||
import {
|
import {
|
||||||
scrollListToBottom,
|
scrollListToBottom,
|
||||||
scrollListToMessage,
|
scrollListToMessage,
|
||||||
@ -560,12 +561,17 @@ export default class ChatChannel extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.chatApi.sendMessage(this.args.channel.id, {
|
const params = {
|
||||||
message: message.message,
|
message: message.message,
|
||||||
in_reply_to_id: message.inReplyTo?.id,
|
in_reply_to_id: message.inReplyTo?.id,
|
||||||
staged_id: message.id,
|
staged_id: message.id,
|
||||||
upload_ids: message.uploads.map((upload) => upload.id),
|
upload_ids: message.uploads.map((upload) => upload.id),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
await this.chatApi.sendMessage(
|
||||||
|
this.args.channel.id,
|
||||||
|
Object.assign({}, params, extractCurrentTopicInfo(this))
|
||||||
|
);
|
||||||
|
|
||||||
if (!this.capabilities.isIOS) {
|
if (!this.capabilities.isIOS) {
|
||||||
this.scrollToLatestMessage();
|
this.scrollToLatestMessage();
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
} from "discourse/plugins/chat/discourse/lib/chat-ios-hacks";
|
} from "discourse/plugins/chat/discourse/lib/chat-ios-hacks";
|
||||||
import ChatMessagesLoader from "discourse/plugins/chat/discourse/lib/chat-messages-loader";
|
import ChatMessagesLoader from "discourse/plugins/chat/discourse/lib/chat-messages-loader";
|
||||||
import DatesSeparatorsPositioner from "discourse/plugins/chat/discourse/lib/dates-separators-positioner";
|
import DatesSeparatorsPositioner from "discourse/plugins/chat/discourse/lib/dates-separators-positioner";
|
||||||
|
import { extractCurrentTopicInfo } from "discourse/plugins/chat/discourse/lib/extract-current-topic-info";
|
||||||
import {
|
import {
|
||||||
scrollListToBottom,
|
scrollListToBottom,
|
||||||
scrollListToMessage,
|
scrollListToMessage,
|
||||||
@ -429,15 +430,17 @@ export default class ChatThread extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const params = {
|
||||||
|
message: message.message,
|
||||||
|
in_reply_to_id: null,
|
||||||
|
staged_id: message.id,
|
||||||
|
upload_ids: message.uploads.map((upload) => upload.id),
|
||||||
|
thread_id: message.thread.id,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await this.chatApi.sendMessage(
|
const response = await this.chatApi.sendMessage(
|
||||||
this.args.thread.channel.id,
|
this.args.thread.channel.id,
|
||||||
{
|
Object.assign({}, params, extractCurrentTopicInfo(this))
|
||||||
message: message.message,
|
|
||||||
in_reply_to_id: null,
|
|
||||||
staged_id: message.id,
|
|
||||||
upload_ids: message.uploads.map((upload) => upload.id),
|
|
||||||
thread_id: message.thread.id,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.args.thread.currentUserMembership ??=
|
this.args.thread.currentUserMembership ??=
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import { getOwner } from "@ember/application";
|
||||||
|
|
||||||
|
export function extractCurrentTopicInfo(context) {
|
||||||
|
const topic = getOwner(context).lookup("controller:topic")?.get("model");
|
||||||
|
|
||||||
|
if (!topic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = { topic_id: topic.id };
|
||||||
|
const currentPostNumber = parseInt(topic.current_post_number, 10);
|
||||||
|
const posts = topic.postStream.posts;
|
||||||
|
|
||||||
|
const currentPost = posts.find(
|
||||||
|
(post) => post.post_number === currentPostNumber
|
||||||
|
);
|
||||||
|
const previousPost = posts.findLast(
|
||||||
|
(post) =>
|
||||||
|
!post.hidden && !post.deleted_at && post.post_number < currentPostNumber
|
||||||
|
);
|
||||||
|
const nextPost = posts.find(
|
||||||
|
(post) =>
|
||||||
|
!post.hidden && !post.deleted_at && post.post_number > currentPostNumber
|
||||||
|
);
|
||||||
|
|
||||||
|
info.post_ids = [previousPost?.id, currentPost?.id, nextPost?.id];
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
@ -163,6 +163,8 @@ export default {
|
|||||||
.lookup("service:chat-api")
|
.lookup("service:chat-api")
|
||||||
.sendMessage(channelId, {
|
.sendMessage(channelId, {
|
||||||
thread_id: options.threadId,
|
thread_id: options.threadId,
|
||||||
|
post_ids: options.postIds,
|
||||||
|
topic_id: options.topicId,
|
||||||
message: options.message,
|
message: options.message,
|
||||||
uploads: options.uploads,
|
uploads: options.uploads,
|
||||||
});
|
});
|
||||||
|
@ -180,6 +180,8 @@ export default class ChatApi extends Service {
|
|||||||
* @param {number} [data.in_reply_to_id] - The ID of the replied-to message.
|
* @param {number} [data.in_reply_to_id] - The ID of the replied-to message.
|
||||||
* @param {number} [data.staged_id] - The staged ID of the message before it was persisted.
|
* @param {number} [data.staged_id] - The staged ID of the message before it was persisted.
|
||||||
* @param {number} [data.thread_id] - The ID of the thread where this message should be posted.
|
* @param {number} [data.thread_id] - The ID of the thread where this message should be posted.
|
||||||
|
* @param {number} [data.topic_id] - The ID of the currently visible topic in drawer mode.
|
||||||
|
* @param {number} [data.post_ids] - The ID of the currently visible posts in drawer mode.
|
||||||
* @param {Array.<number>} [data.upload_ids] - Array of upload ids linked to the message.
|
* @param {Array.<number>} [data.upload_ids] - Array of upload ids linked to the message.
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
@ -11,10 +11,20 @@ module Chat
|
|||||||
case value
|
case value
|
||||||
when String
|
when String
|
||||||
value.split(",")
|
value.split(",")
|
||||||
|
when ::Array
|
||||||
|
value.map { |item| convert_to_integer(item) }
|
||||||
else
|
else
|
||||||
::Array.wrap(value)
|
::Array.wrap(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def convert_to_integer(item)
|
||||||
|
Integer(item)
|
||||||
|
rescue ArgumentError
|
||||||
|
item
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -24,6 +24,22 @@ RSpec.describe Chat::Types::Array do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when 'value' is an array of numbers as string" do
|
||||||
|
let(:value) { %w[1 2] }
|
||||||
|
|
||||||
|
it "returns it with string casted as integer" do
|
||||||
|
expect(casted_value).to eq([1, 2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when 'value' is an array of numbers" do
|
||||||
|
let(:value) { [1, 2] }
|
||||||
|
|
||||||
|
it "returns it" do
|
||||||
|
expect(casted_value).to eq([1, 2])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "when 'value' is something else" do
|
context "when 'value' is something else" do
|
||||||
let(:value) { Time.current }
|
let(:value) { Time.current }
|
||||||
|
|
||||||
|
@ -31,8 +31,17 @@ RSpec.describe Chat::CreateMessage do
|
|||||||
|
|
||||||
let(:guardian) { user.guardian }
|
let(:guardian) { user.guardian }
|
||||||
let(:content) { "A new message @#{other_user.username_lower}" }
|
let(:content) { "A new message @#{other_user.username_lower}" }
|
||||||
|
let(:context_topic_id) { nil }
|
||||||
|
let(:context_post_ids) { nil }
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{ guardian: guardian, chat_channel_id: channel.id, message: content, upload_ids: [upload.id] }
|
{
|
||||||
|
guardian: guardian,
|
||||||
|
chat_channel_id: channel.id,
|
||||||
|
message: content,
|
||||||
|
upload_ids: [upload.id],
|
||||||
|
context_topic_id: context_topic_id,
|
||||||
|
context_post_ids: context_post_ids,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
let(:message) { result[:message_instance].reload }
|
let(:message) { result[:message_instance].reload }
|
||||||
|
|
||||||
@ -91,10 +100,29 @@ RSpec.describe Chat::CreateMessage do
|
|||||||
instance_of(Chat::Message),
|
instance_of(Chat::Message),
|
||||||
channel,
|
channel,
|
||||||
user,
|
user,
|
||||||
|
anything,
|
||||||
)
|
)
|
||||||
|
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when context given" do
|
||||||
|
let(:context_post_ids) { [1, 2] }
|
||||||
|
let(:context_topic_id) { 3 }
|
||||||
|
|
||||||
|
it "triggers a Discourse event with context if given" do
|
||||||
|
DiscourseEvent.expects(:trigger).with(
|
||||||
|
:chat_message_created,
|
||||||
|
instance_of(Chat::Message),
|
||||||
|
channel,
|
||||||
|
user,
|
||||||
|
{ context: { post_ids: context_post_ids, topic_id: context_topic_id } },
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "processes the direct message channel" do
|
it "processes the direct message channel" do
|
||||||
Chat::Action::PublishAndFollowDirectMessageChannel.expects(:call).with(
|
Chat::Action::PublishAndFollowDirectMessageChannel.expects(:call).with(
|
||||||
channel_membership: membership,
|
channel_membership: membership,
|
||||||
|
@ -151,4 +151,56 @@ RSpec.describe "Drawer", type: :system do
|
|||||||
expect(drawer_page).to have_open_channel(channel)
|
expect(drawer_page).to have_open_channel(channel)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when sending a message from topic" do
|
||||||
|
fab!(:topic)
|
||||||
|
fab!(:posts) { Fabricate.times(5, :post, topic: topic) }
|
||||||
|
fab!(:channel) { Fabricate(:chat_channel) }
|
||||||
|
fab!(:membership) do
|
||||||
|
Fabricate(:user_chat_channel_membership, user: current_user, chat_channel: channel)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||||
|
|
||||||
|
context "when on a channel" do
|
||||||
|
it "has context" do
|
||||||
|
::Chat::CreateMessage
|
||||||
|
.expects(:call)
|
||||||
|
.with do |value|
|
||||||
|
value["topic_id"] === topic.id.to_s &&
|
||||||
|
value["post_ids"] === [posts[1].id.to_s, posts[2].id.to_s, posts[3].id.to_s]
|
||||||
|
end
|
||||||
|
|
||||||
|
topic_page.visit_topic(topic, post_number: 3)
|
||||||
|
chat_page.open_from_header
|
||||||
|
drawer_page.open_channel(channel)
|
||||||
|
channel_page.send_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when on a thread" do
|
||||||
|
before { channel.update!(threading_enabled: true) }
|
||||||
|
|
||||||
|
fab!(:thread_1) { Fabricate(:chat_thread, channel: channel) }
|
||||||
|
|
||||||
|
let(:thread_list_page) { PageObjects::Components::Chat::ThreadList.new }
|
||||||
|
let(:thread_page) { PageObjects::Pages::ChatThread.new }
|
||||||
|
|
||||||
|
it "has context" do
|
||||||
|
::Chat::CreateMessage
|
||||||
|
.expects(:call)
|
||||||
|
.with do |value|
|
||||||
|
value["topic_id"] === topic.id.to_s &&
|
||||||
|
value["post_ids"] === [posts[1].id.to_s, posts[2].id.to_s, posts[3].id.to_s]
|
||||||
|
end
|
||||||
|
|
||||||
|
topic_page.visit_topic(topic, post_number: 3)
|
||||||
|
chat_page.open_from_header
|
||||||
|
drawer_page.open_channel(channel)
|
||||||
|
drawer_page.open_thread_list
|
||||||
|
thread_list_page.open_thread(thread_1)
|
||||||
|
thread_page.send_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user