diff --git a/app/assets/javascripts/discourse/app/models/nav-item.js b/app/assets/javascripts/discourse/app/models/nav-item.js index 5f74cb6818..6c1fe8213c 100644 --- a/app/assets/javascripts/discourse/app/models/nav-item.js +++ b/app/assets/javascripts/discourse/app/models/nav-item.js @@ -75,12 +75,13 @@ const NavItem = EmberObject.extend({ "name", "category", "tagId", + "noSubcategories", "topicTrackingState.messageCount" ) - count(name, category, tagId) { + count(name, category, tagId, noSubcategories) { const state = this.topicTrackingState; if (state) { - return state.lookupCount(name, category, tagId); + return state.lookupCount(name, category, tagId, noSubcategories); } }, }); diff --git a/app/assets/javascripts/discourse/app/models/topic-tracking-state.js b/app/assets/javascripts/discourse/app/models/topic-tracking-state.js index b121ae7dd4..5cb98038dd 100644 --- a/app/assets/javascripts/discourse/app/models/topic-tracking-state.js +++ b/app/assets/javascripts/discourse/app/models/topic-tracking-state.js @@ -466,8 +466,10 @@ const TopicTrackingState = EmberObject.extend({ return new Set(result); }, - countCategoryByState(type, categoryId, tagId) { - const subcategoryIds = this.getSubCategoryIds(categoryId); + countCategoryByState(type, categoryId, tagId, noSubcategories) { + const subcategoryIds = noSubcategories + ? new Set() + : this.getSubCategoryIds(categoryId); const mutedCategoryIds = this.currentUser && this.currentUser.muted_category_ids; let filter = type === "new" ? isNew : isUnread; @@ -485,12 +487,17 @@ const TopicTrackingState = EmberObject.extend({ ).length; }, - countNew(categoryId, tagId) { - return this.countCategoryByState("new", categoryId, tagId); + countNew(categoryId, tagId, noSubcategories) { + return this.countCategoryByState("new", categoryId, tagId, noSubcategories); }, - countUnread(categoryId, tagId) { - return this.countCategoryByState("unread", categoryId, tagId); + countUnread(categoryId, tagId, noSubcategories) { + return this.countCategoryByState( + "unread", + categoryId, + tagId, + noSubcategories + ); }, forEachTracked(fn) { @@ -548,20 +555,20 @@ const TopicTrackingState = EmberObject.extend({ return sum; }, - lookupCount(name, category, tagId) { + lookupCount(name, category, tagId, noSubcategories) { if (name === "latest") { return ( - this.lookupCount("new", category, tagId) + - this.lookupCount("unread", category, tagId) + this.lookupCount("new", category, tagId, noSubcategories) + + this.lookupCount("unread", category, tagId, noSubcategories) ); } let categoryId = category ? get(category, "id") : null; if (name === "new") { - return this.countNew(categoryId, tagId); + return this.countNew(categoryId, tagId, noSubcategories); } else if (name === "unread") { - return this.countUnread(categoryId, tagId); + return this.countUnread(categoryId, tagId, noSubcategories); } else { const categoryName = name.split("/")[1]; if (categoryName) { diff --git a/app/assets/javascripts/discourse/app/routes/build-category-route.js b/app/assets/javascripts/discourse/app/routes/build-category-route.js index 59c9046a7c..8672db77c1 100644 --- a/app/assets/javascripts/discourse/app/routes/build-category-route.js +++ b/app/assets/javascripts/discourse/app/routes/build-category-route.js @@ -20,42 +20,7 @@ export default (filterArg, params) => { return DiscourseRoute.extend({ queryParams, - serialize(modelParams) { - if (!modelParams.category_slug_path_with_id) { - if (modelParams.id === "none") { - const category_slug_path_with_id = [ - modelParams.parentSlug, - modelParams.slug, - ].join("/"); - const category = Category.findBySlugPathWithID( - category_slug_path_with_id - ); - this.replaceWith("discovery.categoryNone", { - category, - category_slug_path_with_id, - }); - } else if (modelParams.id === "all") { - modelParams.category_slug_path_with_id = [ - modelParams.parentSlug, - modelParams.slug, - ].join("/"); - } else { - modelParams.category_slug_path_with_id = [ - modelParams.parentSlug, - modelParams.slug, - modelParams.id, - ] - .filter((x) => x) - .join("/"); - } - } - - return modelParams; - }, - model(modelParams) { - modelParams = this.serialize(modelParams); - const category = Category.findBySlugPathWithID( modelParams.category_slug_path_with_id ); @@ -88,12 +53,11 @@ export default (filterArg, params) => { const { category, modelParams } = model; if ( + (!params || params.no_subcategories === undefined) && category.default_list_filter === "none" && - filterArg === "default" && - modelParams && - modelParams.id !== "all" + filterArg === "default" ) { - this.replaceWith("discovery.categoryNone", { + return this.replaceWith("discovery.categoryNone", { category, category_slug_path_with_id: modelParams.category_slug_path_with_id, }); @@ -137,11 +101,14 @@ export default (filterArg, params) => { }, _retrieveTopicList(category, transition, modelParams) { - const listFilter = `c/${Category.slugFor(category)}/${ - category.id - }/l/${this.filter(category)}`, - findOpts = filterQueryParams(modelParams, params), - extras = { cached: this.isPoppedState(transition) }; + const findOpts = filterQueryParams(modelParams, params); + const extras = { cached: this.isPoppedState(transition) }; + + let listFilter = `c/${Category.slugFor(category)}/${category.id}`; + if (findOpts.no_subcategories) { + listFilter += "/none"; + } + listFilter += `/l/${this.filter(category)}`; return findTopicList( this.store, diff --git a/app/models/topic_list.rb b/app/models/topic_list.rb index 41bc79f46e..6d24e8ba5b 100644 --- a/app/models/topic_list.rb +++ b/app/models/topic_list.rb @@ -64,7 +64,11 @@ class TopicList < DraftableList def preload_key if @category - "topic_list_#{@category.url.sub(/^\//, '')}/l/#{@filter}" + if @opts[:no_subcategories] + "topic_list_#{@category.url.sub(/^\//, '')}/none/l/#{@filter}" + else + "topic_list_#{@category.url.sub(/^\//, '')}/l/#{@filter}" + end else "topic_list_#{@filter}" end diff --git a/lib/topic_query_params.rb b/lib/topic_query_params.rb index bab30135a1..0676334bf6 100644 --- a/lib/topic_query_params.rb +++ b/lib/topic_query_params.rb @@ -16,9 +16,7 @@ module TopicQueryParams # hacky columns get special handling options[:topic_ids] = param_to_integer_list(:topic_ids) - if options[:no_subcategories] == 'true' - options[:no_subcategories] = true - end + options[:no_subcategories] = options[:no_subcategories] == 'true' if options[:no_subcategories].present? options end diff --git a/spec/models/topic_list_spec.rb b/spec/models/topic_list_spec.rb index fbf27ac901..f731bdce5f 100644 --- a/spec/models/topic_list_spec.rb +++ b/spec/models/topic_list_spec.rb @@ -90,4 +90,18 @@ describe TopicList do end end end + + describe "#preload_key" do + let(:category) { Fabricate(:category) } + + it "generates correct key for categories" do + topic_list = TopicList.new('latest', nil, nil, category: category, category_id: category.id) + expect(topic_list.preload_key).to eq("topic_list_c/#{category.slug}/#{category.id}/l/latest") + end + + it "generates correct key for 'no subcategories' option" do + topic_list = TopicList.new('latest', nil, nil, category: category, category_id: category.id, no_subcategories: true) + expect(topic_list.preload_key).to eq("topic_list_c/#{category.slug}/#{category.id}/none/l/latest") + end + end end diff --git a/spec/requests/list_controller_spec.rb b/spec/requests/list_controller_spec.rb index a7db647bd8..0d793e8a07 100644 --- a/spec/requests/list_controller_spec.rb +++ b/spec/requests/list_controller_spec.rb @@ -88,6 +88,9 @@ RSpec.describe ListController do end describe "categories and X" do + let(:category) { Fabricate(:category_with_definition) } + let(:sub_category) { Fabricate(:category_with_definition, parent_category: category) } + it "returns top topics" do Fabricate(:topic, like_count: 1000, posts_count: 100) TopTopic.refresh! @@ -100,6 +103,12 @@ RSpec.describe ListController do data = response.parsed_body expect(data["topic_list"]["topics"].length).to eq(2) end + + it "returns topics from subcategories when no_subcategories=false" do + Fabricate(:topic, category: sub_category) + get "/c/#{category.slug}/#{category.id}/l/latest.json?no_subcategories=false" + expect(response.parsed_body["topic_list"]["topics"].length).to eq(2) + end end describe 'titles for crawler layout' do