mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 16:11:08 +08:00
FEATURE: Add threads support to chat archives (#24325)
This PR introduces thread support for channel archives. Now, threaded messages are rendered inside a `details` HTML tag in posts. The transcript markdown rules now support two new attributes: `threadId` and `threadTitle`. - If `threadId` is present, all nested `chat` tags are rendered inside the first one. - `threadTitle` (optional) defines the summary content. ``` [chat threadId=19 ... ] thread OM [chat ... ] thread reply [/chat] [/chat] ``` If threads are split across multiple posts when archiving, the range of messages in each part will be displayed alongside the thread title. For example: `(message 1 to 16 of 20)` and `(message 17 to 20 of 20)`.
This commit is contained in:
@ -90,6 +90,13 @@ describe Chat::ChannelArchiveService do
|
||||
num.times { Fabricate(:chat_message, chat_channel: channel) }
|
||||
end
|
||||
|
||||
def create_threaded_messages(num, title: nil)
|
||||
original_message = Fabricate(:chat_message, chat_channel: channel)
|
||||
thread =
|
||||
Fabricate(:chat_thread, channel: channel, title: title, original_message: original_message)
|
||||
(num - 1).times { Fabricate(:chat_message, chat_channel: channel, thread: thread) }
|
||||
end
|
||||
|
||||
def start_archive
|
||||
@channel_archive =
|
||||
described_class.create_archive_process(
|
||||
@ -143,6 +150,61 @@ describe Chat::ChannelArchiveService do
|
||||
expect(@channel_archive.chat_channel.chat_messages.count).to eq(0)
|
||||
end
|
||||
|
||||
it "creates the correct posts for a channel with messages and threads" do
|
||||
create_messages(2)
|
||||
create_threaded_messages(6, title: "a new thread")
|
||||
create_messages(7)
|
||||
create_threaded_messages(3)
|
||||
create_threaded_messages(27, title: "another long thread")
|
||||
create_messages(10)
|
||||
|
||||
start_archive
|
||||
|
||||
stub_const(Chat::ChannelArchiveService, "ARCHIVED_MESSAGES_PER_POST", 5) do
|
||||
described_class.new(@channel_archive).execute
|
||||
end
|
||||
|
||||
@channel_archive.reload
|
||||
topic = @channel_archive.destination_topic
|
||||
expect(topic.posts.count).to eq(14)
|
||||
|
||||
topic
|
||||
.posts
|
||||
.where.not(post_number: 1)
|
||||
.each do |post|
|
||||
case post.post_number
|
||||
when 2
|
||||
expect(post.raw).to include("a new thread")
|
||||
expect(post.raw).to include(
|
||||
I18n.t("chat.transcript.split_thread_range", start: 1, end: 2, total: 5),
|
||||
)
|
||||
when 3
|
||||
expect(post.raw).to include("a new thread")
|
||||
expect(post.raw).to include(
|
||||
I18n.t("chat.transcript.split_thread_range", start: 3, end: 5, total: 5),
|
||||
)
|
||||
when 5
|
||||
expect(post.raw).to include(
|
||||
"threadTitle=\"#{I18n.t("chat.transcript.default_thread_title")}\"",
|
||||
)
|
||||
when 10
|
||||
expect(post.raw).to include("another long thread")
|
||||
expect(post.raw).to include(
|
||||
I18n.t("chat.transcript.split_thread_range", start: 17, end: 20, total: 26),
|
||||
)
|
||||
end
|
||||
|
||||
expect(post.raw).to include("[chat")
|
||||
expect(post.raw).to include("noLink=\"true\"")
|
||||
expect(post.user).to eq(Discourse.system_user)
|
||||
end
|
||||
expect(topic.archived).to eq(true)
|
||||
|
||||
expect(@channel_archive.archived_messages).to eq(55)
|
||||
expect(@channel_archive.chat_channel.status).to eq("archived")
|
||||
expect(@channel_archive.chat_channel.chat_messages.count).to eq(0)
|
||||
end
|
||||
|
||||
it "does not stop the process if the post length is too high (validations disabled)" do
|
||||
create_messages(50) && start_archive
|
||||
SiteSetting.max_post_length = 1
|
||||
|
@ -6,7 +6,9 @@ describe Chat::TranscriptService do
|
||||
let(:acting_user) { Fabricate(:user) }
|
||||
let(:user1) { Fabricate(:user, username: "martinchat") }
|
||||
let(:user2) { Fabricate(:user, username: "brucechat") }
|
||||
let(:channel) { Fabricate(:category_channel, name: "The Beam Discussions") }
|
||||
let(:channel) do
|
||||
Fabricate(:category_channel, name: "The Beam Discussions", threading_enabled: true)
|
||||
end
|
||||
|
||||
def service(message_ids, opts: {})
|
||||
described_class.new(channel, acting_user, messages_or_ids: Array.wrap(message_ids), opts: opts)
|
||||
@ -254,4 +256,201 @@ describe Chat::TranscriptService do
|
||||
[/chat]
|
||||
MARKDOWN
|
||||
end
|
||||
|
||||
it "generates reaction data for threaded messages" do
|
||||
thread = Fabricate(:chat_thread, channel: channel)
|
||||
thread_om =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
user: user1,
|
||||
chat_channel: channel,
|
||||
thread: thread,
|
||||
message: "an extremely insightful response :)",
|
||||
)
|
||||
thread_reply_1 =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user2,
|
||||
thread: thread,
|
||||
message: "wow so tru",
|
||||
)
|
||||
thread_reply_2 =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user1,
|
||||
thread: thread,
|
||||
message: "a new perspective",
|
||||
)
|
||||
|
||||
Chat::MessageReaction.create!(
|
||||
chat_message: thread_om,
|
||||
user: Fabricate(:user, username: "bjorn"),
|
||||
emoji: "heart",
|
||||
)
|
||||
Chat::MessageReaction.create!(
|
||||
chat_message: thread_reply_1,
|
||||
user: Fabricate(:user, username: "sigurd"),
|
||||
emoji: "heart",
|
||||
)
|
||||
Chat::MessageReaction.create!(
|
||||
chat_message: thread_reply_1,
|
||||
user: Fabricate(:user, username: "hvitserk"),
|
||||
emoji: "+1",
|
||||
)
|
||||
Chat::MessageReaction.create!(
|
||||
chat_message: thread_reply_2,
|
||||
user: Fabricate(:user, username: "ubbe"),
|
||||
emoji: "money_mouth_face",
|
||||
)
|
||||
|
||||
rendered =
|
||||
service(
|
||||
[thread_om.id, thread_reply_1.id, thread_reply_2.id],
|
||||
opts: {
|
||||
include_reactions: true,
|
||||
},
|
||||
).generate_markdown
|
||||
expect(rendered).to eq(<<~MARKDOWN)
|
||||
[chat quote="martinchat;#{thread_om.id};#{thread_om.created_at.iso8601}" channel="The Beam Discussions" channelId="#{channel.id}" multiQuote="true" chained="true" reactions="heart:bjorn" threadId="#{thread.id}" threadTitle="#{I18n.t("chat.transcript.default_thread_title")}"]
|
||||
an extremely insightful response :)
|
||||
|
||||
[chat quote="brucechat;#{thread_reply_1.id};#{thread_reply_1.created_at.iso8601}" chained="true" reactions="+1:hvitserk;heart:sigurd"]
|
||||
wow so tru
|
||||
[/chat]
|
||||
|
||||
[chat quote="martinchat;#{thread_reply_2.id};#{thread_reply_2.created_at.iso8601}" chained="true" reactions="money_mouth_face:ubbe"]
|
||||
a new perspective
|
||||
[/chat]
|
||||
|
||||
[/chat]
|
||||
MARKDOWN
|
||||
end
|
||||
|
||||
it "generates a chat transcript for threaded messages" do
|
||||
thread = Fabricate(:chat_thread, channel: channel)
|
||||
thread_om =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user1,
|
||||
thread: thread,
|
||||
message: "reply to me!",
|
||||
)
|
||||
thread_reply_1 =
|
||||
Fabricate(:chat_message, chat_channel: channel, user: user2, thread: thread, message: "done")
|
||||
thread_reply_2 =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user1,
|
||||
thread: thread,
|
||||
message: "thanks",
|
||||
)
|
||||
|
||||
rendered = service([thread_om.id, thread_reply_1.id, thread_reply_2.id]).generate_markdown
|
||||
expect(rendered).to eq(<<~MARKDOWN)
|
||||
[chat quote="martinchat;#{thread_om.id};#{thread_om.created_at.iso8601}" channel="The Beam Discussions" channelId="#{channel.id}" multiQuote="true" chained="true" threadId="#{thread.id}" threadTitle="#{I18n.t("chat.transcript.default_thread_title")}"]
|
||||
reply to me!
|
||||
|
||||
[chat quote="brucechat;#{thread_reply_1.id};#{thread_reply_1.created_at.iso8601}" chained="true"]
|
||||
done
|
||||
[/chat]
|
||||
|
||||
[chat quote="martinchat;#{thread_reply_2.id};#{thread_reply_2.created_at.iso8601}" chained="true"]
|
||||
thanks
|
||||
[/chat]
|
||||
|
||||
[/chat]
|
||||
MARKDOWN
|
||||
end
|
||||
|
||||
it "generates the correct markdown for multiple threads" do
|
||||
channel_message_1 =
|
||||
Fabricate(:chat_message, user: user1, chat_channel: channel, message: "I need ideas")
|
||||
thread_1 = Fabricate(:chat_thread, channel: channel)
|
||||
thread_1_om =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user2,
|
||||
thread: thread_1,
|
||||
message: "this is my idea",
|
||||
)
|
||||
thread_1_message =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user1,
|
||||
thread: thread_1,
|
||||
message: "cool",
|
||||
)
|
||||
|
||||
channel_message_2 =
|
||||
Fabricate(:chat_message, user: user2, chat_channel: channel, message: "more?")
|
||||
thread_2 = Fabricate(:chat_thread, channel: channel, title: "the second idea")
|
||||
thread_2_om =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user2,
|
||||
thread: thread_2,
|
||||
message: "another one",
|
||||
)
|
||||
thread_2_message_1 =
|
||||
Fabricate(
|
||||
:chat_message,
|
||||
chat_channel: channel,
|
||||
user: user1,
|
||||
thread: thread_2,
|
||||
message: "thanks",
|
||||
)
|
||||
thread_2_message_2 =
|
||||
Fabricate(:chat_message, chat_channel: channel, user: user2, thread: thread_2, message: "np")
|
||||
|
||||
rendered =
|
||||
service(
|
||||
[
|
||||
channel_message_1.id,
|
||||
thread_1_om.id,
|
||||
thread_1_message.id,
|
||||
channel_message_2.id,
|
||||
thread_2_om.id,
|
||||
thread_2_message_1.id,
|
||||
thread_2_message_2.id,
|
||||
],
|
||||
).generate_markdown
|
||||
expect(rendered).to eq(<<~MARKDOWN)
|
||||
[chat quote="martinchat;#{channel_message_1.id};#{channel_message_1.created_at.iso8601}" channel="The Beam Discussions" channelId="#{channel.id}" multiQuote="true" chained="true"]
|
||||
I need ideas
|
||||
[/chat]
|
||||
|
||||
[chat quote="brucechat;#{thread_1_om.id};#{thread_1_om.created_at.iso8601}" chained="true" threadId="#{thread_1.id}" threadTitle="#{I18n.t("chat.transcript.default_thread_title")}"]
|
||||
this is my idea
|
||||
|
||||
[chat quote="martinchat;#{thread_1_message.id};#{thread_1_message.created_at.iso8601}" chained="true"]
|
||||
cool
|
||||
[/chat]
|
||||
|
||||
[/chat]
|
||||
|
||||
[chat quote="brucechat;#{channel_message_2.id};#{channel_message_2.created_at.iso8601}" chained="true"]
|
||||
more?
|
||||
[/chat]
|
||||
|
||||
[chat quote="brucechat;#{thread_2_om.id};#{thread_2_om.created_at.iso8601}" chained="true" threadId="#{thread_2.id}" threadTitle="the second idea"]
|
||||
another one
|
||||
|
||||
[chat quote="martinchat;#{thread_2_message_1.id};#{thread_2_message_1.created_at.iso8601}" chained="true"]
|
||||
thanks
|
||||
[/chat]
|
||||
|
||||
[chat quote="brucechat;#{thread_2_message_2.id};#{thread_2_message_2.created_at.iso8601}" chained="true"]
|
||||
np
|
||||
[/chat]
|
||||
|
||||
[/chat]
|
||||
MARKDOWN
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user