FIX: correct tracking when mute all categories (#11441)

Currently, we have a solution for muted topics. Basically, when a post is created first we send a `muted` message to users who muted that specific topic:

https://github.com/discourse/discourse/blob/master/app/models/topic_tracking_state.rb#L91

Later, topic tracking state filters if the topic is muted or not before update state:

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/models/topic-tracking-state.js#L58:L67

That solution works quite well.

I wanted to extend it to handle `mute all categories by default` setting as well.

In that case, we should only inform the user about new topic/post when they explicitly want to.

If that setting is enabled, we would send "unmuted" message to a user who watches specific category, topic or tag. In all other cases, don't inform user about new topic as all categories are muted by default.

Meta: https://meta.discourse.org/t/threads-muted-by-mute-all-by-default-are-showing-up-as-new-but-not-visible/168324
This commit is contained in:
Krzysztof Kotlarek
2020-12-10 16:49:05 +11:00
committed by GitHub
parent eb60fc86dc
commit da2a61e36c
6 changed files with 123 additions and 15 deletions
@@ -55,17 +55,24 @@ const TopicTrackingState = EmberObject.extend({
const tracker = this;
const process = (data) => {
if (data.message_type === "muted") {
tracker.trackMutedTopic(data.topic_id);
if (["muted", "unmuted"].includes(data.message_type)) {
tracker.trackMutedOrUnmutedTopic(data);
return;
}
tracker.pruneOldMutedTopics();
tracker.pruneOldMutedAndUnmutedTopics();
if (tracker.isMutedTopic(data.topic_id)) {
return;
}
if (
this.siteSettings.mute_all_categories_by_default &&
!tracker.isUnmutedTopic(data.topic_id)
) {
return;
}
if (data.message_type === "delete") {
tracker.removeTopic(data.topic_id);
tracker.incrementMessageCount();
@@ -166,26 +173,47 @@ const TopicTrackingState = EmberObject.extend({
return (this.currentUser && this.currentUser.muted_topics) || [];
},
trackMutedTopic(topicId) {
let mutedTopics = this.mutedTopics().concat({
topicId: topicId,
createdAt: Date.now(),
});
this.currentUser && this.currentUser.set("muted_topics", mutedTopics);
unmutedTopics() {
return (this.currentUser && this.currentUser.unmuted_topics) || [];
},
pruneOldMutedTopics() {
trackMutedOrUnmutedTopic(data) {
let topics, key;
if (data.message_type === "muted") {
key = "muted_topics";
topics = this.mutedTopics();
} else {
key = "unmuted_topics";
topics = this.unmutedTopics();
}
topics = topics.concat({
topicId: data.topic_id,
createdAt: Date.now(),
});
this.currentUser && this.currentUser.set(key, topics);
},
pruneOldMutedAndUnmutedTopics() {
const now = Date.now();
let mutedTopics = this.mutedTopics().filter(
(mutedTopic) => now - mutedTopic.createdAt < 60000
);
this.currentUser && this.currentUser.set("muted_topics", mutedTopics);
let unmutedTopics = this.unmutedTopics().filter(
(unmutedTopic) => now - unmutedTopic.createdAt < 60000
);
this.currentUser &&
this.currentUser.set("muted_topics", mutedTopics) &&
this.currentUser.set("unmuted_topics", unmutedTopics);
},
isMutedTopic(topicId) {
return !!this.mutedTopics().findBy("topicId", topicId);
},
isUnmutedTopic(topicId) {
return !!this.unmutedTopics().findBy("topicId", topicId);
},
updateSeen(topicId, highestSeen) {
if (!topicId || !highestSeen) {
return;
@@ -327,7 +327,7 @@ module("Unit | Model | topic-tracking-state", function (hooks) {
assert.equal(state.countNew(4), 0);
});
test("mute topic", function (assert) {
test("mute and unmute topic", function (assert) {
let currentUser = User.create({
username: "chuck",
muted_category_ids: [],
@@ -335,14 +335,19 @@ module("Unit | Model | topic-tracking-state", function (hooks) {
const state = TopicTrackingState.create({ currentUser });
state.trackMutedTopic(1);
state.trackMutedOrUnmutedTopic({ topic_id: 1, message_type: "muted" });
assert.equal(currentUser.muted_topics[0].topicId, 1);
state.pruneOldMutedTopics();
state.trackMutedOrUnmutedTopic({ topic_id: 2, message_type: "unmuted" });
assert.equal(currentUser.unmuted_topics[0].topicId, 2);
state.pruneOldMutedAndUnmutedTopics();
assert.equal(state.isMutedTopic(1), true);
assert.equal(state.isUnmutedTopic(2), true);
this.clock.tick(60000);
state.pruneOldMutedTopics();
state.pruneOldMutedAndUnmutedTopics();
assert.equal(state.isMutedTopic(1), false);
assert.equal(state.isUnmutedTopic(2), false);
});
});