FIX: Muted tags are respected by TopicTrackingState (#8467)

When the tag is muted and topic contains that tag, we should not mark that message as NEW.

There are 3 possible settings which site admin can set.
remove_muted_tags_from_latest - always
It means that if the topic got at least one muted tag, we should not mark that topic as NEW

remove_muted_tags_from_latest - only muted
Similar to above, however, if at least one tag is not muted, the topic is marked as NEW

remove_muted_tags_from_latest - never
Basically, mute tag setting is ignored and all topics are set as NEW
This commit is contained in:
Krzysztof Kotlarek 2019-12-10 09:50:05 +11:00 committed by GitHub
parent 6740e08caa
commit 81c7d6a462
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 140 additions and 4 deletions

View File

@ -31,6 +31,18 @@ function isUnseen(topic) {
return !topic.is_seen;
}
function hasMutedTags(topicTagIds, mutedTagIds) {
if (!mutedTagIds || !topicTagIds) {
return false;
}
return (
(Discourse.SiteSettings.remove_muted_tags_from_latest === "always" &&
topicTagIds.any(tagId => mutedTagIds.includes(tagId))) ||
(Discourse.SiteSettings.remove_muted_tags_from_latest === "only_muted" &&
topicTagIds.every(tagId => mutedTagIds.includes(tagId)))
);
}
const TopicTrackingState = EmberObject.extend({
messageCount: 0,
@ -60,6 +72,13 @@ const TopicTrackingState = EmberObject.extend({
}
}
if (["new_topic", "latest"].includes(data.message_type)) {
const mutedTagIds = User.currentProp("muted_tag_ids");
if (hasMutedTags(data.payload.topic_tag_ids, mutedTagIds)) {
return;
}
}
// fill parent_category_id we need it for counting new/unread
if (data.payload && data.payload.category_id) {
var category = Category.findById(data.payload.category_id);

View File

@ -33,7 +33,8 @@ class TopicTrackingState
created_at: topic.created_at,
topic_id: topic.id,
category_id: topic.category_id,
archetype: topic.archetype
archetype: topic.archetype,
topic_tag_ids: topic.tags.pluck(:id)
}
}
@ -52,7 +53,8 @@ class TopicTrackingState
payload: {
bumped_at: topic.bumped_at,
category_id: topic.category_id,
archetype: topic.archetype
archetype: topic.archetype,
topic_tag_ids: topic.tags.pluck(:id)
}
}
@ -182,7 +184,8 @@ class TopicTrackingState
skip_order: true,
staff: user.staff?,
admin: user.admin?,
user: user
user: user,
muted_tag_ids: muted_tag_ids(user)
)
sql << "\nUNION ALL\n\n"
@ -194,7 +197,8 @@ class TopicTrackingState
staff: user.staff?,
filter_old_unread: true,
admin: user.admin?,
user: user
user: user,
muted_tag_ids: muted_tag_ids(user)
)
DB.query(
@ -205,6 +209,10 @@ class TopicTrackingState
)
end
def self.muted_tag_ids(user)
TagUser.lookup(user, :muted).pluck(:tag_id)
end
def self.report_raw_sql(opts = nil)
opts ||= {}
@ -268,6 +276,19 @@ class TopicTrackingState
"(topics.visible #{append}) AND"
end
tags_filter =
if opts[:muted_tag_ids].present? && SiteSetting.remove_muted_tags_from_latest == 'always'
<<~SQL
NOT ((select array_agg(tag_id) from topic_tags where topic_tags.topic_id = topics.id) && ARRAY[#{opts[:muted_tag_ids].join(',')}]) AND
SQL
elsif opts[:muted_tag_ids].present? && SiteSetting.remove_muted_tags_from_latest == 'only_muted'
<<~SQL
NOT ((select array_agg(tag_id) from topic_tags where topic_tags.topic_id = topics.id) <@ ARRAY[#{opts[:muted_tag_ids].join(',')}]) AND
SQL
else
""
end
sql = +<<~SQL
SELECT #{select}
FROM topics
@ -282,6 +303,7 @@ class TopicTrackingState
topics.archetype <> 'private_message' AND
((#{unread}) OR (#{new})) AND
#{visibility_filter}
#{tags_filter}
topics.deleted_at IS NULL AND
#{category_filter}
(category_users.id IS NULL OR

View File

@ -27,6 +27,7 @@ class CurrentUserSerializer < BasicUserSerializer
:redirected_to_top,
:custom_fields,
:muted_category_ids,
:muted_tag_ids,
:dismissed_banner_key,
:is_anonymous,
:reviewable_count,
@ -169,6 +170,10 @@ class CurrentUserSerializer < BasicUserSerializer
CategoryUser.lookup(object, :muted).pluck(:category_id)
end
def muted_tag_ids
TagUser.lookup(object, :muted).pluck(:tag_id)
end
def ignored_users
IgnoredUser.where(user: object.id).joins(:ignored_user).pluck(:username)
end

View File

@ -353,6 +353,77 @@ describe TopicTrackingState do
expect(report.length).to eq(1)
end
context 'muted tags' do
it "remove_muted_tags_from_latest is set to always" do
SiteSetting.remove_muted_tags_from_latest = 'always'
user = Fabricate(:user)
tag1 = Fabricate(:tag)
tag2 = Fabricate(:tag)
Fabricate(:topic_tag, tag: tag1, topic: topic)
Fabricate(:topic_tag, tag: tag2, topic: topic)
post
report = TopicTrackingState.report(user)
expect(report.length).to eq(1)
TagUser.create!(user_id: user.id,
notification_level: TagUser.notification_levels[:muted],
tag_id: tag1.id
)
report = TopicTrackingState.report(user)
expect(report.length).to eq(0)
end
it "remove_muted_tags_from_latest is set to only_muted" do
SiteSetting.remove_muted_tags_from_latest = 'only_muted'
user = Fabricate(:user)
tag1 = Fabricate(:tag)
tag2 = Fabricate(:tag)
Fabricate(:topic_tag, tag: tag1, topic: topic)
Fabricate(:topic_tag, tag: tag2, topic: topic)
post
report = TopicTrackingState.report(user)
expect(report.length).to eq(1)
TagUser.create!(user_id: user.id,
notification_level: TagUser.notification_levels[:muted],
tag_id: tag1.id
)
report = TopicTrackingState.report(user)
expect(report.length).to eq(1)
TagUser.create!(user_id: user.id,
notification_level: TagUser.notification_levels[:muted],
tag_id: tag2.id
)
report = TopicTrackingState.report(user)
expect(report.length).to eq(0)
end
it "remove_muted_tags_from_latest is set to never" do
SiteSetting.remove_muted_tags_from_latest = 'never'
user = Fabricate(:user)
tag1 = Fabricate(:tag)
Fabricate(:topic_tag, tag: tag1, topic: topic)
post
report = TopicTrackingState.report(user)
expect(report.length).to eq(1)
TagUser.create!(user_id: user.id,
notification_level: TagUser.notification_levels[:muted],
tag_id: tag1.id
)
report = TopicTrackingState.report(user)
expect(report.length).to eq(1)
end
end
it "correctly handles seen categories" do
user = Fabricate(:user)
post

View File

@ -68,6 +68,25 @@ RSpec.describe CurrentUserSerializer do
end
end
context "#muted_tag_ids" do
fab!(:user) { Fabricate(:user) }
fab!(:tag) { Fabricate(:tag) }
let!(:tag_user) do
TagUser.create!(user_id: user.id,
notification_level: TagUser.notification_levels[:muted],
tag_id: tag.id
)
end
let :serializer do
CurrentUserSerializer.new(user, scope: Guardian.new, root: false)
end
it 'include muted tag ids' do
payload = serializer.as_json
expect(payload[:muted_tag_ids]).to eq([tag.id])
end
end
context "#second_factor_enabled" do
fab!(:user) { Fabricate(:user) }
let :serializer do