From c95ffb98efb2554e67d4011098144a1f0da4ed15 Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Tue, 17 Oct 2023 19:06:01 +0300 Subject: [PATCH] DEV: Serialize categories in topic lists (#23597) At this moment, this feature is under a site setting named lazy_load_categories. In the future, categories will no longer be preloaded through site data. This commit add information about categories in topic list and ensures that data is used to display topic list items. Parent categories are serialized too because they are necessary to render {{category-link}}. --- .../discourse/app/models/topic-list.js | 7 ++++ app/models/topic_list.rb | 11 ++++++- app/serializers/topic_category_serializer.rb | 32 +++++++++++++++++++ app/serializers/topic_list_serializer.rb | 5 +++ config/site_settings.yml | 3 ++ spec/requests/list_controller_spec.rb | 32 +++++++++++++++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 app/serializers/topic_category_serializer.rb diff --git a/app/assets/javascripts/discourse/app/models/topic-list.js b/app/assets/javascripts/discourse/app/models/topic-list.js index bb56578ce17..93ca8a55f9d 100644 --- a/app/assets/javascripts/discourse/app/models/topic-list.js +++ b/app/assets/javascripts/discourse/app/models/topic-list.js @@ -5,6 +5,7 @@ import { isEmpty } from "@ember/utils"; import { Promise } from "rsvp"; import { ajax } from "discourse/lib/ajax"; import RestModel from "discourse/models/rest"; +import Site from "discourse/models/site"; import User from "discourse/models/user"; import deprecated from "discourse-common/lib/deprecated"; import { getOwnerWithFallback } from "discourse-common/lib/get-owner"; @@ -168,6 +169,12 @@ TopicList.reopenClass({ const users = extractByKey(result.users, User); const groups = extractByKey(result.primary_groups, EmberObject); + if (result.topic_list.categories) { + result.topic_list.categories.forEach((c) => { + Site.current().updateCategory(c); + }); + } + return result.topic_list[listKey].map((t) => { t.posters.forEach((p) => { p.user = users[p.user_id]; diff --git a/app/models/topic_list.rb b/app/models/topic_list.rb index de50712dc79..4e021e87369 100644 --- a/app/models/topic_list.rb +++ b/app/models/topic_list.rb @@ -76,6 +76,10 @@ class TopicList @topics ||= load_topics end + def categories + @categories ||= topics.map { |t| [t.category, t.category.parent_category] }.uniq.flatten.compact + end + def load_topics @topics = @topics_input @@ -128,7 +132,12 @@ class TopicList ft.topic_list = self end - topic_preloader_associations = [:image_upload, { topic_thumbnails: :optimized_image }] + topic_preloader_associations = [ + :image_upload, + { topic_thumbnails: :optimized_image }, + { category: :parent_category }, + ] + topic_preloader_associations.concat(DiscoursePluginRegistry.topic_preloader_associations.to_a) ActiveRecord::Associations::Preloader.new( diff --git a/app/serializers/topic_category_serializer.rb b/app/serializers/topic_category_serializer.rb new file mode 100644 index 00000000000..e2961eb84c2 --- /dev/null +++ b/app/serializers/topic_category_serializer.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class TopicCategorySerializer < ApplicationSerializer + attributes :id, + :name, + :color, + :text_color, + :slug, + :description_text, + :read_restricted, + :parent_category_id + + def include_parent_category_id? + parent_category_id + end + + def name + if object.uncategorized? + I18n.t("uncategorized_category_name", locale: SiteSetting.default_locale) + else + object.name + end + end + + def description_text + if object.uncategorized? + I18n.t("category.uncategorized_description", locale: SiteSetting.default_locale) + else + object.description_text + end + end +end diff --git a/app/serializers/topic_list_serializer.rb b/app/serializers/topic_list_serializer.rb index 5e78f344a3e..814118877d9 100644 --- a/app/serializers/topic_list_serializer.rb +++ b/app/serializers/topic_list_serializer.rb @@ -12,6 +12,7 @@ class TopicListSerializer < ApplicationSerializer has_many :topics, serializer: TopicListItemSerializer, embed: :objects has_many :shared_drafts, serializer: TopicListItemSerializer, embed: :objects has_many :tags, serializer: TagSerializer, embed: :objects + has_many :categories, serializer: TopicCategorySerializer, embed: :objects def can_create_topic scope.can_create?(Topic) @@ -36,4 +37,8 @@ class TopicListSerializer < ApplicationSerializer def include_tags? SiteSetting.tagging_enabled && object.tags.present? end + + def include_categories? + SiteSetting.lazy_load_categories + end end diff --git a/config/site_settings.yml b/config/site_settings.yml index 307624f556a..bab5c11bae8 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2174,6 +2174,9 @@ developer: instrument_gc_stat_per_request: default: false hidden: true + lazy_load_categories: + default: false + hidden: true navigation: navigation_menu: diff --git a/spec/requests/list_controller_spec.rb b/spec/requests/list_controller_spec.rb index dae18310b1c..0590c26a648 100644 --- a/spec/requests/list_controller_spec.rb +++ b/spec/requests/list_controller_spec.rb @@ -223,6 +223,38 @@ RSpec.describe ListController do expect(response.body).not_to include(restricted_tag.name) end end + + context "with lazy_load_categories" do + fab!(:category) { Fabricate(:category) } + fab!(:subcategory) { Fabricate(:category, parent_category: category) } + + before { topic.update!(category: subcategory) } + + it "returns categories and parent categories if true" do + SiteSetting.lazy_load_categories = true + + get "/latest.json" + + expect(response.status).to eq(200) + expect(response.parsed_body["topic_list"]["topics"].length).to eq(1) + expect(response.parsed_body["topic_list"]["topics"][0]["id"]).to eq(topic.id) + expect(response.parsed_body["topic_list"]["categories"].length).to eq(2) + expect( + response.parsed_body["topic_list"]["categories"].map { |c| c["id"] }, + ).to contain_exactly(category.id, subcategory.id) + end + + it "does not return categories if not true" do + SiteSetting.lazy_load_categories = false + + get "/latest.json" + + expect(response.status).to eq(200) + expect(response.parsed_body["topic_list"]["topics"].length).to eq(1) + expect(response.parsed_body["topic_list"]["topics"][0]["id"]).to eq(topic.id) + expect(response.parsed_body["topic_list"]["categories"]).to eq(nil) + end + end end describe "categories and X" do