FIX: Don't publish PM archive events to acting user. (#14291)

When a user archives a personal message, they are redirected back to the
inbox and will refresh the list of the topics for the given filter.
Publishing an event to the user results in an incorrect incoming message
because the list of topics has already been refreshed.

This does mean that if a user has two tabs opened, the non-active tab
will not receive the incoming message but at this point we do not think
the technical trade-offs are worth it to support this feature. We
basically have to somehow exclude a client from an incoming message
which is not easy to do.

Follow-up to fc1fd1b416
This commit is contained in:
Alan Guo Xiang Tan 2021-09-10 09:20:50 +08:00 committed by GitHub
parent 2215cc2547
commit bc23dcd30b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 117 additions and 114 deletions

View File

@ -168,18 +168,12 @@ const PrivateMessageTopicTrackingState = EmberObject.extend({
this._notifyIncoming(message.topic_id); this._notifyIncoming(message.topic_id);
} }
break;
case "archive":
if (
[INBOX_FILTER, ARCHIVE_FILTER].includes(this.filter) &&
["user", "all"].includes(this.inbox)
) {
this._notifyIncoming(message.topic_id);
}
break; break;
case "group_archive": case "group_archive":
if ( if (
[INBOX_FILTER, ARCHIVE_FILTER].includes(this.filter) && [INBOX_FILTER, ARCHIVE_FILTER].includes(this.filter) &&
(!message.payload.acting_user_id ||
message.payload.acting_user_id !== this.currentUser.id) &&
(this.inbox === "all" || this._displayMessageForGroupInbox(message)) (this.inbox === "all" || this._displayMessageForGroupInbox(message))
) { ) {
this._notifyIncoming(message.topic_id); this._notifyIncoming(message.topic_id);

View File

@ -235,16 +235,6 @@ acceptance(
); );
}; };
const publishArchiveToMessageBus = function (opts) {
publishToMessageBus(
`/private-message-topic-tracking-state/user/${opts.userId || 5}`,
{
topic_id: opts.topicId,
message_type: "archive",
}
);
};
const publishGroupArchiveToMessageBus = function (opts) { const publishGroupArchiveToMessageBus = function (opts) {
publishToMessageBus( publishToMessageBus(
`/private-message-topic-tracking-state/group/${opts.groupIds[0]}`, `/private-message-topic-tracking-state/group/${opts.groupIds[0]}`,
@ -253,6 +243,7 @@ acceptance(
message_type: "group_archive", message_type: "group_archive",
payload: { payload: {
group_ids: opts.groupIds, group_ids: opts.groupIds,
acting_user_id: opts.actingUserId,
}, },
} }
); );
@ -289,42 +280,6 @@ acceptance(
); );
}; };
test("incoming archive message on all and archive filter", async function (assert) {
for (const url of [
"/u/charlie/messages",
"/u/charlie/messages/archive",
"/u/charlie/messages/personal",
"/u/charlie/messages/personal/archive",
]) {
await visit(url);
publishArchiveToMessageBus({ topicId: 1 });
await visit(url); // wait for re-render
assert.ok(
exists(".show-mores"),
`${url} displays the topic incoming info`
);
}
for (const url of [
"/u/charlie/messages/group/awesome_group/archive",
"/u/charlie/messages/group/awesome_group",
]) {
await visit(url);
publishArchiveToMessageBus({ topicId: 1 });
await visit(url); // wait for re-render
assert.ok(
!exists(".show-mores"),
`${url} does not display the topic incoming info`
);
}
});
test("incoming read message on unread filter", async function (assert) { test("incoming read message on unread filter", async function (assert) {
await visit("/u/charlie/messages/unread"); await visit("/u/charlie/messages/unread");
@ -335,6 +290,23 @@ acceptance(
assert.ok(exists(".show-mores"), `displays the topic incoming info`); assert.ok(exists(".show-mores"), `displays the topic incoming info`);
}); });
test("incoming group archive message acted by current user", async function (assert) {
await visit("/u/charlie/messages");
publishGroupArchiveToMessageBus({
groupIds: [14],
topicId: 1,
actingUserId: 5,
});
await visit("/u/charlie/messages"); // wait for re-render
assert.ok(
!exists(".show-mores"),
`does not display the topic incoming info`
);
});
test("incoming group archive message on all and archive filter", async function (assert) { test("incoming group archive message on all and archive filter", async function (assert) {
for (const url of [ for (const url of [
"/u/charlie/messages", "/u/charlie/messages",

View File

@ -548,12 +548,22 @@ class TopicsController < ApplicationController
if group_ids.present? if group_ids.present?
allowed_groups = topic.allowed_groups allowed_groups = topic.allowed_groups
.where('topic_allowed_groups.group_id IN (?)', group_ids).pluck(:id) .where('topic_allowed_groups.group_id IN (?)', group_ids).pluck(:id)
allowed_groups.each do |id| allowed_groups.each do |id|
if archive if archive
GroupArchivedMessage.archive!(id, topic) GroupArchivedMessage.archive!(
id,
topic,
acting_user_id: current_user.id
)
group_id = id group_id = id
else else
GroupArchivedMessage.move_to_inbox!(id, topic) GroupArchivedMessage.move_to_inbox!(
id,
topic,
acting_user_id: current_user.id
)
end end
end end
end end

View File

@ -9,7 +9,7 @@ class GroupArchivedMessage < ActiveRecord::Base
destroyed = GroupArchivedMessage.where(group_id: group_id, topic_id: topic_id).destroy_all destroyed = GroupArchivedMessage.where(group_id: group_id, topic_id: topic_id).destroy_all
trigger(:move_to_inbox, group_id, topic_id) trigger(:move_to_inbox, group_id, topic_id)
MessageBus.publish("/topic/#{topic_id}", { type: "move_to_inbox" }, group_ids: [group_id]) MessageBus.publish("/topic/#{topic_id}", { type: "move_to_inbox" }, group_ids: [group_id])
publish_topic_tracking_state(topic, group_id) publish_topic_tracking_state(topic, group_id, opts[:acting_user_id])
set_imap_sync(topic_id) if !opts[:skip_imap_sync] && destroyed.present? set_imap_sync(topic_id) if !opts[:skip_imap_sync] && destroyed.present?
end end
@ -19,7 +19,7 @@ class GroupArchivedMessage < ActiveRecord::Base
GroupArchivedMessage.create!(group_id: group_id, topic_id: topic_id) GroupArchivedMessage.create!(group_id: group_id, topic_id: topic_id)
trigger(:archive_message, group_id, topic_id) trigger(:archive_message, group_id, topic_id)
MessageBus.publish("/topic/#{topic_id}", { type: "archived" }, group_ids: [group_id]) MessageBus.publish("/topic/#{topic_id}", { type: "archived" }, group_ids: [group_id])
publish_topic_tracking_state(topic, group_id) publish_topic_tracking_state(topic, group_id, opts[:acting_user_id])
set_imap_sync(topic_id) if !opts[:skip_imap_sync] && destroyed.blank? set_imap_sync(topic_id) if !opts[:skip_imap_sync] && destroyed.blank?
end end
@ -39,8 +39,12 @@ class GroupArchivedMessage < ActiveRecord::Base
end end
private_class_method :set_imap_sync private_class_method :set_imap_sync
def self.publish_topic_tracking_state(topic, group_id) def self.publish_topic_tracking_state(topic, group_id, acting_user_id = nil)
PrivateMessageTopicTrackingState.publish_group_archived(topic, group_id) PrivateMessageTopicTrackingState.publish_group_archived(
topic: topic,
group_id: group_id,
acting_user_id: acting_user_id
)
end end
private_class_method :publish_topic_tracking_state private_class_method :publish_topic_tracking_state
end end

View File

@ -21,7 +21,6 @@ class PrivateMessageTopicTrackingState
NEW_MESSAGE_TYPE = "new_topic" NEW_MESSAGE_TYPE = "new_topic"
UNREAD_MESSAGE_TYPE = "unread" UNREAD_MESSAGE_TYPE = "unread"
READ_MESSAGE_TYPE = "read" READ_MESSAGE_TYPE = "read"
ARCHIVE_MESSAGE_TYPE = "archive"
GROUP_ARCHIVE_MESSAGE_TYPE = "group_archive" GROUP_ARCHIVE_MESSAGE_TYPE = "group_archive"
def self.report(user) def self.report(user)
@ -163,29 +162,23 @@ class PrivateMessageTopicTrackingState
end end
end end
def self.publish_group_archived(topic, group_id) def self.publish_group_archived(topic:, group_id:, acting_user_id: nil)
return unless topic.private_message? return unless topic.private_message?
message = { message = {
message_type: GROUP_ARCHIVE_MESSAGE_TYPE, message_type: GROUP_ARCHIVE_MESSAGE_TYPE,
topic_id: topic.id, topic_id: topic.id,
payload: { payload: {
group_ids: [group_id] group_ids: [group_id],
acting_user_id: acting_user_id
} }
}.as_json }.as_json
MessageBus.publish(self.group_channel(group_id), message, group_ids: [group_id]) MessageBus.publish(
end self.group_channel(group_id),
message,
def self.publish_user_archived(topic, user_id) group_ids: [group_id]
return unless topic.private_message? )
message = {
message_type: ARCHIVE_MESSAGE_TYPE,
topic_id: topic.id,
}.as_json
MessageBus.publish(self.user_channel(user_id), message, user_ids: [user_id])
end end
def self.publish_read(topic_id, last_read_post_number, user, notification_level = nil) def self.publish_read(topic_id, last_read_post_number, user, notification_level = nil)

View File

@ -16,7 +16,6 @@ class UserArchivedMessage < ActiveRecord::Base
UserArchivedMessage.where(user_id: user_id, topic_id: topic_id).destroy_all UserArchivedMessage.where(user_id: user_id, topic_id: topic_id).destroy_all
trigger(:move_to_inbox, user_id, topic_id) trigger(:move_to_inbox, user_id, topic_id)
MessageBus.publish("/topic/#{topic_id}", { type: "move_to_inbox" }, user_ids: [user_id]) MessageBus.publish("/topic/#{topic_id}", { type: "move_to_inbox" }, user_ids: [user_id])
publish_topic_tracking_state(topic, user_id)
end end
def self.archive!(user_id, topic) def self.archive!(user_id, topic)
@ -25,7 +24,6 @@ class UserArchivedMessage < ActiveRecord::Base
UserArchivedMessage.create!(user_id: user_id, topic_id: topic_id) UserArchivedMessage.create!(user_id: user_id, topic_id: topic_id)
trigger(:archive_message, user_id, topic_id) trigger(:archive_message, user_id, topic_id)
MessageBus.publish("/topic/#{topic_id}", { type: "archived" }, user_ids: [user_id]) MessageBus.publish("/topic/#{topic_id}", { type: "archived" }, user_ids: [user_id])
publish_topic_tracking_state(topic, user_id)
end end
def self.trigger(event, user_id, topic_id) def self.trigger(event, user_id, topic_id)
@ -35,11 +33,6 @@ class UserArchivedMessage < ActiveRecord::Base
DiscourseEvent.trigger(event, user: user, topic: topic) DiscourseEvent.trigger(event, user: user, topic: topic)
end end
end end
def self.publish_topic_tracking_state(topic, user_id)
PrivateMessageTopicTrackingState.publish_user_archived(topic, user_id)
end
private_class_method :publish_topic_tracking_state
end end
# == Schema Information # == Schema Information

View File

@ -480,7 +480,7 @@ class PostCreator
end end
GroupArchivedMessage.where(topic_id: @topic.id).pluck(:group_id).each do |group_id| GroupArchivedMessage.where(topic_id: @topic.id).pluck(:group_id).each do |group_id|
GroupArchivedMessage.move_to_inbox!(group_id, @topic) GroupArchivedMessage.move_to_inbox!(group_id, @topic, acting_user_id: @user.id)
end end
end end

View File

@ -47,7 +47,7 @@ class TopicsBulkAction
topics.each do |t| topics.each do |t|
if guardian.can_see?(t) && t.private_message? if guardian.can_see?(t) && t.private_message?
if group if group
GroupArchivedMessage.move_to_inbox!(group.id, t) GroupArchivedMessage.move_to_inbox!(group.id, t, acting_user_id: @user.id)
else else
UserArchivedMessage.move_to_inbox!(@user.id, t) UserArchivedMessage.move_to_inbox!(@user.id, t)
end end
@ -60,7 +60,7 @@ class TopicsBulkAction
topics.each do |t| topics.each do |t|
if guardian.can_see?(t) && t.private_message? if guardian.can_see?(t) && t.private_message?
if group if group
GroupArchivedMessage.archive!(group.id, t) GroupArchivedMessage.archive!(group.id, t, acting_user_id: @user.id)
else else
UserArchivedMessage.archive!(@user.id, t) UserArchivedMessage.archive!(@user.id, t)
end end

View File

@ -1134,6 +1134,26 @@ describe PostCreator do
expect(target_user1.notifications.count).to eq(1) expect(target_user1.notifications.count).to eq(1)
expect(target_user2.notifications.count).to eq(1) expect(target_user2.notifications.count).to eq(1)
GroupArchivedMessage.create!(group: group, topic: post.topic)
message = MessageBus.track_publish(
PrivateMessageTopicTrackingState.group_channel(group.id)
) do
PostCreator.create!(user,
raw: "this is a reply to the group message",
topic_id: post.topic_id
)
end.first
expect(message.data["message_type"]).to eq(
PrivateMessageTopicTrackingState::GROUP_ARCHIVE_MESSAGE_TYPE
)
expect(message.data["payload"]["acting_user_id"]).to eq(user.id)
expect(GroupArchivedMessage.exists?(group: group, topic: post.topic))
.to eq(false)
end end
end end

View File

@ -168,26 +168,17 @@ describe PrivateMessageTopicTrackingState do
end end
end end
describe '.publish_user_archived' do
it 'should publish the right message_bus message' do
message = MessageBus.track_publish(described_class.user_channel(user.id)) do
described_class.publish_user_archived(private_message, user.id)
end.first
data = message.data
expect(data['topic_id']).to eq(private_message.id)
expect(data['message_type']).to eq(described_class::ARCHIVE_MESSAGE_TYPE)
end
end
describe '.publish_group_archived' do describe '.publish_group_archived' do
it 'should publish the right message_bus message' do it 'should publish the right message_bus message' do
user_3 = Fabricate(:user) user_3 = Fabricate(:user)
group.add(user_3) group.add(user_3)
messages = MessageBus.track_publish do messages = MessageBus.track_publish do
described_class.publish_group_archived(group_message, group.id) described_class.publish_group_archived(
topic: group_message,
group_id: group.id,
acting_user_id: user_3.id
)
end end
expect(messages.map(&:channel)).to contain_exactly( expect(messages.map(&:channel)).to contain_exactly(
@ -201,6 +192,7 @@ describe PrivateMessageTopicTrackingState do
expect(data['message_type']).to eq(described_class::GROUP_ARCHIVE_MESSAGE_TYPE) expect(data['message_type']).to eq(described_class::GROUP_ARCHIVE_MESSAGE_TYPE)
expect(data['topic_id']).to eq(group_message.id) expect(data['topic_id']).to eq(group_message.id)
expect(data['payload']['group_ids']).to contain_exactly(group.id) expect(data['payload']['group_ids']).to contain_exactly(group.id)
expect(data['payload']['acting_user_id']).to eq(user_3.id)
end end
end end

View File

@ -20,11 +20,7 @@ describe UserArchivedMessage do
UserArchivedMessage.archive!(user.id, private_message) UserArchivedMessage.archive!(user.id, private_message)
expect do expect do
messages = MessageBus.track_publish(PrivateMessageTopicTrackingState.user_channel(user.id)) do
UserArchivedMessage.move_to_inbox!(user.id, private_message) UserArchivedMessage.move_to_inbox!(user.id, private_message)
end
expect(messages.present?).to eq(true)
end.to change { private_message.message_archived?(user) }.from(true).to(false) end.to change { private_message.message_archived?(user) }.from(true).to(false)
end end
@ -40,14 +36,4 @@ describe UserArchivedMessage do
end end
end end
describe '.archive' do
it 'archives message correctly' do
messages = MessageBus.track_publish(PrivateMessageTopicTrackingState.user_channel(user.id)) do
UserArchivedMessage.archive!(user.id, private_message)
end
expect(messages.present?).to eq(true)
end
end
end end

View File

@ -4458,4 +4458,43 @@ RSpec.describe TopicsController do
.to eq(true) .to eq(true)
end end
end end
describe '#archive_message' do
fab!(:group) do
Fabricate(:group, messageable_level: Group::ALIAS_LEVELS[:everyone]).tap do |g|
g.add(user)
end
end
fab!(:group_message) do
create_post(
user: user,
target_group_names: [group.name],
archetype: Archetype.private_message
).topic
end
it 'should be able to archive a private message' do
sign_in(user)
message = MessageBus.track_publish(
PrivateMessageTopicTrackingState.group_channel(group.id)
) do
put "/t/#{group_message.id}/archive-message.json"
expect(response.status).to eq(200)
end.first
expect(message.data["message_type"]).to eq(
PrivateMessageTopicTrackingState::GROUP_ARCHIVE_MESSAGE_TYPE
)
expect(message.data["payload"]["acting_user_id"]).to eq(user.id)
body = response.parsed_body
expect(body["group_name"]).to eq(group.name)
end
end
end end