FIX: Build correct topic list filter (#11473)

* FIX: 'false' value was treated as a truthy value

For example, latest.json?no_subcategories=false used to have set
no_subcategories to the string value of 'false', which is not false.

* DEV: Remove dead code

* FIX: Redirect to /none under the right conditions

These conditions are:
 - neither /all or /none present
 - only for default filter

* FIX: Build correct topic list filter

/none was never added to the topic list filter

* FIX: Do not show count for subcategories if 'none' category

* FIX: preload_key must contain /none if no_subcategories
This commit is contained in:
Bianca Nenciu 2020-12-11 14:20:48 +02:00 committed by GitHub
parent 36b4712349
commit df26d2e72a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 61 additions and 61 deletions

View File

@ -75,12 +75,13 @@ const NavItem = EmberObject.extend({
"name", "name",
"category", "category",
"tagId", "tagId",
"noSubcategories",
"topicTrackingState.messageCount" "topicTrackingState.messageCount"
) )
count(name, category, tagId) { count(name, category, tagId, noSubcategories) {
const state = this.topicTrackingState; const state = this.topicTrackingState;
if (state) { if (state) {
return state.lookupCount(name, category, tagId); return state.lookupCount(name, category, tagId, noSubcategories);
} }
}, },
}); });

View File

@ -466,8 +466,10 @@ const TopicTrackingState = EmberObject.extend({
return new Set(result); return new Set(result);
}, },
countCategoryByState(type, categoryId, tagId) { countCategoryByState(type, categoryId, tagId, noSubcategories) {
const subcategoryIds = this.getSubCategoryIds(categoryId); const subcategoryIds = noSubcategories
? new Set()
: this.getSubCategoryIds(categoryId);
const mutedCategoryIds = const mutedCategoryIds =
this.currentUser && this.currentUser.muted_category_ids; this.currentUser && this.currentUser.muted_category_ids;
let filter = type === "new" ? isNew : isUnread; let filter = type === "new" ? isNew : isUnread;
@ -485,12 +487,17 @@ const TopicTrackingState = EmberObject.extend({
).length; ).length;
}, },
countNew(categoryId, tagId) { countNew(categoryId, tagId, noSubcategories) {
return this.countCategoryByState("new", categoryId, tagId); return this.countCategoryByState("new", categoryId, tagId, noSubcategories);
}, },
countUnread(categoryId, tagId) { countUnread(categoryId, tagId, noSubcategories) {
return this.countCategoryByState("unread", categoryId, tagId); return this.countCategoryByState(
"unread",
categoryId,
tagId,
noSubcategories
);
}, },
forEachTracked(fn) { forEachTracked(fn) {
@ -548,20 +555,20 @@ const TopicTrackingState = EmberObject.extend({
return sum; return sum;
}, },
lookupCount(name, category, tagId) { lookupCount(name, category, tagId, noSubcategories) {
if (name === "latest") { if (name === "latest") {
return ( return (
this.lookupCount("new", category, tagId) + this.lookupCount("new", category, tagId, noSubcategories) +
this.lookupCount("unread", category, tagId) this.lookupCount("unread", category, tagId, noSubcategories)
); );
} }
let categoryId = category ? get(category, "id") : null; let categoryId = category ? get(category, "id") : null;
if (name === "new") { if (name === "new") {
return this.countNew(categoryId, tagId); return this.countNew(categoryId, tagId, noSubcategories);
} else if (name === "unread") { } else if (name === "unread") {
return this.countUnread(categoryId, tagId); return this.countUnread(categoryId, tagId, noSubcategories);
} else { } else {
const categoryName = name.split("/")[1]; const categoryName = name.split("/")[1];
if (categoryName) { if (categoryName) {

View File

@ -20,42 +20,7 @@ export default (filterArg, params) => {
return DiscourseRoute.extend({ return DiscourseRoute.extend({
queryParams, 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) { model(modelParams) {
modelParams = this.serialize(modelParams);
const category = Category.findBySlugPathWithID( const category = Category.findBySlugPathWithID(
modelParams.category_slug_path_with_id modelParams.category_slug_path_with_id
); );
@ -88,12 +53,11 @@ export default (filterArg, params) => {
const { category, modelParams } = model; const { category, modelParams } = model;
if ( if (
(!params || params.no_subcategories === undefined) &&
category.default_list_filter === "none" && category.default_list_filter === "none" &&
filterArg === "default" && filterArg === "default"
modelParams &&
modelParams.id !== "all"
) { ) {
this.replaceWith("discovery.categoryNone", { return this.replaceWith("discovery.categoryNone", {
category, category,
category_slug_path_with_id: modelParams.category_slug_path_with_id, category_slug_path_with_id: modelParams.category_slug_path_with_id,
}); });
@ -137,11 +101,14 @@ export default (filterArg, params) => {
}, },
_retrieveTopicList(category, transition, modelParams) { _retrieveTopicList(category, transition, modelParams) {
const listFilter = `c/${Category.slugFor(category)}/${ const findOpts = filterQueryParams(modelParams, params);
category.id const extras = { cached: this.isPoppedState(transition) };
}/l/${this.filter(category)}`,
findOpts = filterQueryParams(modelParams, params), let listFilter = `c/${Category.slugFor(category)}/${category.id}`;
extras = { cached: this.isPoppedState(transition) }; if (findOpts.no_subcategories) {
listFilter += "/none";
}
listFilter += `/l/${this.filter(category)}`;
return findTopicList( return findTopicList(
this.store, this.store,

View File

@ -64,7 +64,11 @@ class TopicList < DraftableList
def preload_key def preload_key
if @category if @category
if @opts[:no_subcategories]
"topic_list_#{@category.url.sub(/^\//, '')}/none/l/#{@filter}"
else
"topic_list_#{@category.url.sub(/^\//, '')}/l/#{@filter}" "topic_list_#{@category.url.sub(/^\//, '')}/l/#{@filter}"
end
else else
"topic_list_#{@filter}" "topic_list_#{@filter}"
end end

View File

@ -16,9 +16,7 @@ module TopicQueryParams
# hacky columns get special handling # hacky columns get special handling
options[:topic_ids] = param_to_integer_list(:topic_ids) options[:topic_ids] = param_to_integer_list(:topic_ids)
if options[:no_subcategories] == 'true' options[:no_subcategories] = options[:no_subcategories] == 'true' if options[:no_subcategories].present?
options[:no_subcategories] = true
end
options options
end end

View File

@ -90,4 +90,18 @@ describe TopicList do
end end
end 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 end

View File

@ -88,6 +88,9 @@ RSpec.describe ListController do
end end
describe "categories and X" do 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 it "returns top topics" do
Fabricate(:topic, like_count: 1000, posts_count: 100) Fabricate(:topic, like_count: 1000, posts_count: 100)
TopTopic.refresh! TopTopic.refresh!
@ -100,6 +103,12 @@ RSpec.describe ListController do
data = response.parsed_body data = response.parsed_body
expect(data["topic_list"]["topics"].length).to eq(2) expect(data["topic_list"]["topics"].length).to eq(2)
end 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 end
describe 'titles for crawler layout' do describe 'titles for crawler layout' do