FEATURE: Create and update thread memberships (#21501)

When the user sends a message in a thread, we want to
create a membership for them in the background (default
to notification level of Watching) so we can track whether
they have read the thread.

Then, for now since we don't have granular message reading/
scrolling in the thread panel, we just update the thread
last_read_message_id for the user to the latest reply in the
thread when they open the thread panel. This at least will
mark the thread as read.

In future PRs we want to show the blue dot indicator in various
places in the UI for unread threads which will also require
some MessageBus functionality.

This takes into account the same issue fixed for channels
in ae3231e140
This commit is contained in:
Martin Brennan
2023-05-11 14:35:26 +02:00
committed by GitHub
parent b837459e1d
commit 26f9ccd8bb
9 changed files with 249 additions and 4 deletions
@@ -0,0 +1,83 @@
# frozen_string_literal: true
RSpec.describe Chat::Action::ResetUserLastReadThreadMessage do
fab!(:thread_1) { Fabricate(:chat_thread) }
fab!(:thread_2) { Fabricate(:chat_thread) }
fab!(:message_1) do
Fabricate(
:chat_message,
chat_channel: thread_1.channel,
thread: thread_1,
created_at: 1.hour.ago,
)
end
fab!(:message_2) do
Fabricate(
:chat_message,
chat_channel: thread_1.channel,
thread: thread_1,
created_at: 2.seconds.ago,
)
end
fab!(:message_3) do
Fabricate(
:chat_message,
chat_channel: thread_1.channel,
thread: thread_1,
created_at: 3.minutes.ago,
)
end
fab!(:message_4) do
Fabricate(
:chat_message,
chat_channel: thread_2.channel,
thread: thread_2,
created_at: 30.seconds.ago,
)
end
fab!(:message_5) do
Fabricate(
:chat_message,
chat_channel: thread_2.channel,
thread: thread_2,
created_at: 3.seconds.ago,
)
end
fab!(:message_6) do
Fabricate(
:chat_message,
chat_channel: thread_2.channel,
thread: thread_2,
created_at: 1.day.ago,
)
end
fab!(:membership_1) do
Fabricate(:user_chat_thread_membership, thread: thread_1, last_read_message_id: message_3.id)
end
fab!(:membership_2) do
Fabricate(:user_chat_thread_membership, thread: thread_2, last_read_message_id: message_6.id)
end
context "when there are non-deleted messages left in the thread" do
before do
message_3.trash!
message_6.trash!
end
it "sets the matching membership last_read_message_ids to the most recently created message ID" do
described_class.call([message_3.id, message_6.id], [thread_1.id, thread_2.id])
expect(membership_1.reload.last_read_message_id).to eq(message_2.id)
expect(membership_2.reload.last_read_message_id).to eq(message_5.id)
end
end
context "when there are no more non-deleted messages left in the thread (excluding the original message)" do
before { [message_1, message_2, message_4, message_5].each(&:trash!) }
it "sets the matching membership last_read_message_ids to NULL" do
described_class.call([message_3.id, message_6.id], [thread_1.id, thread_2.id])
expect(membership_1.reload.last_read_message_id).to be_nil
expect(membership_2.reload.last_read_message_id).to be_nil
end
end
end
@@ -119,6 +119,35 @@ RSpec.describe Chat::TrashMessage do
result
expect(thread.replies_count_cache).to eq(4)
end
it "updates the tracking to the last non-deleted thread message for users whose last_read_message_id was the trashed message" do
other_message =
Fabricate(:chat_message, chat_channel: message.chat_channel, thread: thread)
membership_1 =
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
membership_2 =
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
membership_3 =
Fabricate(
:user_chat_thread_membership,
thread: thread,
last_read_message: other_message,
)
result
expect(membership_1.reload.last_read_message_id).to eq(other_message.id)
expect(membership_2.reload.last_read_message_id).to eq(other_message.id)
expect(membership_3.reload.last_read_message_id).to eq(other_message.id)
end
it "updates the tracking to nil when there are no other messages left in the thread" do
membership_1 =
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
membership_2 =
Fabricate(:user_chat_thread_membership, thread: thread, last_read_message: message)
result
expect(membership_1.reload.last_read_message_id).to be_nil
expect(membership_2.reload.last_read_message_id).to be_nil
end
end
context "when message is already deleted" do
@@ -12,6 +12,8 @@ RSpec.describe Chat::UpdateUserThreadLastRead do
fab!(:current_user) { Fabricate(:user) }
fab!(:channel) { Fabricate(:chat_channel) }
fab!(:thread) { Fabricate(:chat_thread, channel: channel) }
fab!(:thread_reply_1) { Fabricate(:chat_message, chat_channel: channel, thread: thread) }
fab!(:thread_reply_2) { Fabricate(:chat_message, chat_channel: channel, thread: thread) }
let(:guardian) { Guardian.new(current_user) }
let(:params) { { guardian: guardian, channel_id: channel.id, thread_id: thread.id } }
@@ -85,6 +87,17 @@ RSpec.describe Chat::UpdateUserThreadLastRead do
it "publishes new last read to clients" do
expect(messages.map(&:channel)).to include("/chat/user-tracking-state/#{current_user.id}")
end
context "when the user is a member of the thread" do
fab!(:membership) do
Fabricate(:user_chat_thread_membership, user: current_user, thread: thread)
end
it "updates the last_read_message_id of the thread" do
result
expect(membership.reload.last_read_message_id).to eq(thread.replies.last.id)
end
end
end
end
end