From 7d8c33a094f7887487d9ee900cf216ca5bee02bf Mon Sep 17 00:00:00 2001 From: Dan Ungureanu Date: Fri, 17 Jan 2020 12:02:57 +0200 Subject: [PATCH] FIX: Include sub-sub-categories in new/unread counts (#8710) The count used to be less than the real one because the topics in sub-sub-categories and deeper were not taken into account. --- .../models/topic-tracking-state.js.es6 | 42 +++++----- .../models/topic-tracking-state-test.js.es6 | 77 +++++++++++++++++++ 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 index bf5ed55d8af..8f79703ef71 100644 --- a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 +++ b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 @@ -76,15 +76,6 @@ const TopicTrackingState = EmberObject.extend({ } } - // 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); - - if (category && category.parent_category_id) { - data.payload.parent_category_id = category.parent_category_id; - } - } - if (data.message_type === "latest") { tracker.notify(data); } @@ -364,30 +355,43 @@ const TopicTrackingState = EmberObject.extend({ this.incrementProperty("messageCount"); }, - countNew(category_id) { + getSubCategoryIds(categoryId) { + const result = [categoryId]; + const categories = Category.list(); + + for (let i = 0; i < result.length; ++i) { + for (let j = 0; j < categories.length; ++j) { + if (result[i] === categories[j].parent_category_id) { + result[result.length] = categories[j].id; + } + } + } + + return new Set(result); + }, + + countNew(categoryId) { + const subcategoryIds = this.getSubCategoryIds(categoryId); return _.chain(this.states) .filter(isNew) .filter( topic => topic.archetype !== "private_message" && !topic.deleted && - (topic.category_id === category_id || - topic.parent_category_id === category_id || - !category_id) + (!categoryId || subcategoryIds.has(topic.category_id)) ) .value().length; }, - countUnread(category_id) { + countUnread(categoryId) { + const subcategoryIds = this.getSubCategoryIds(categoryId); return _.chain(this.states) .filter(isUnread) .filter( topic => topic.archetype !== "private_message" && !topic.deleted && - (topic.category_id === category_id || - topic.parent_category_id === category_id || - !category_id) + (!categoryId || subcategoryIds.has(topic.category_id)) ) .value().length; }, @@ -434,10 +438,6 @@ const TopicTrackingState = EmberObject.extend({ // I am taking some shortcuts here to avoid 500 gets for a large list if (data) { data.forEach(topic => { - let category = Category.findById(topic.category_id); - if (category && category.parent_category_id) { - topic.parent_category_id = category.parent_category_id; - } states["t" + topic.topic_id] = topic; }); } diff --git a/test/javascripts/models/topic-tracking-state-test.js.es6 b/test/javascripts/models/topic-tracking-state-test.js.es6 index 1aa73eebb40..0d958b8503d 100644 --- a/test/javascripts/models/topic-tracking-state-test.js.es6 +++ b/test/javascripts/models/topic-tracking-state-test.js.es6 @@ -1,6 +1,7 @@ import TopicTrackingState from "discourse/models/topic-tracking-state"; import createStore from "helpers/create-store"; import Category from "discourse/models/category"; +import { NotificationLevels } from "discourse/lib/notification-levels"; QUnit.module("model:topic-tracking-state"); @@ -91,3 +92,79 @@ QUnit.test("subscribe to category", function(assert) { "expect to properly track incoming for subcategory" ); }); + +QUnit.test("getSubCategoryIds", assert => { + const store = createStore(); + const foo = store.createRecord("category", { id: 1, slug: "foo" }); + const bar = store.createRecord("category", { + id: 2, + slug: "bar", + parent_category_id: foo.id + }); + const baz = store.createRecord("category", { + id: 3, + slug: "baz", + parent_category_id: bar.id + }); + sandbox.stub(Category, "list").returns([foo, bar, baz]); + + const state = TopicTrackingState.create(); + assert.deepEqual(Array.from(state.getSubCategoryIds(1)), [1, 2, 3]); + assert.deepEqual(Array.from(state.getSubCategoryIds(2)), [2, 3]); + assert.deepEqual(Array.from(state.getSubCategoryIds(3)), [3]); +}); + +QUnit.test("countNew", assert => { + const store = createStore(); + const foo = store.createRecord("category", { id: 1, slug: "foo" }); + const bar = store.createRecord("category", { + id: 2, + slug: "bar", + parent_category_id: foo.id + }); + const baz = store.createRecord("category", { + id: 3, + slug: "baz", + parent_category_id: bar.id + }); + sandbox.stub(Category, "list").returns([foo, bar, baz]); + + const state = TopicTrackingState.create(); + + assert.equal(state.countNew(1), 0); + assert.equal(state.countNew(2), 0); + assert.equal(state.countNew(3), 0); + + state.states["t112"] = { + last_read_post_number: null, + id: 112, + notification_level: NotificationLevels.TRACKING, + category_id: 2 + }; + + assert.equal(state.countNew(1), 1); + assert.equal(state.countNew(2), 1); + assert.equal(state.countNew(3), 0); + + state.states["t113"] = { + last_read_post_number: null, + id: 113, + notification_level: NotificationLevels.TRACKING, + category_id: 3 + }; + + assert.equal(state.countNew(1), 2); + assert.equal(state.countNew(2), 2); + assert.equal(state.countNew(3), 1); + + state.states["t111"] = { + last_read_post_number: null, + id: 111, + notification_level: NotificationLevels.TRACKING, + category_id: 1 + }; + + assert.equal(state.countNew(1), 3); + assert.equal(state.countNew(2), 2); + assert.equal(state.countNew(3), 1); +});