diff --git a/app/assets/javascripts/discourse/app/models/user-drafts-stream.js b/app/assets/javascripts/discourse/app/models/user-drafts-stream.js index e95c87c1612..580e9bcc409 100644 --- a/app/assets/javascripts/discourse/app/models/user-drafts-stream.js +++ b/app/assets/javascripts/discourse/app/models/user-drafts-stream.js @@ -8,6 +8,7 @@ import { NEW_TOPIC_KEY, } from "discourse/models/composer"; import RestModel from "discourse/models/rest"; +import Site from "discourse/models/site"; import UserDraft from "discourse/models/user-draft"; import discourseComputed from "discourse-common/utils/decorators"; @@ -64,6 +65,10 @@ export default class UserDraftsStream extends RestModel { return; } + result.categories?.forEach((category) => + Site.current().updateCategory(category) + ); + this.set("hasMore", result.drafts.size >= this.limit); const promises = result.drafts.map((draft) => { diff --git a/app/assets/javascripts/discourse/app/models/user-stream.js b/app/assets/javascripts/discourse/app/models/user-stream.js index af56796de31..39833c7d552 100644 --- a/app/assets/javascripts/discourse/app/models/user-stream.js +++ b/app/assets/javascripts/discourse/app/models/user-stream.js @@ -5,6 +5,7 @@ import { url } from "discourse/lib/computed"; import { emojiUnescape } from "discourse/lib/text"; import { escapeExpression } from "discourse/lib/utilities"; import RestModel from "discourse/models/rest"; +import Site from "discourse/models/site"; import UserAction from "discourse/models/user-action"; import discourseComputed from "discourse-common/utils/decorators"; @@ -110,6 +111,11 @@ export default class UserStream extends RestModel { .then((result) => { if (result && result.user_actions) { const copy = A(); + + result.categories?.forEach((category) => { + Site.current().updateCategory(category); + }); + result.user_actions.forEach((action) => { action.title = emojiUnescape(escapeExpression(action.title)); copy.pushObject(UserAction.create(action)); diff --git a/app/controllers/drafts_controller.rb b/app/controllers/drafts_controller.rb index c9ec8bdab87..16e1b1b1e8b 100644 --- a/app/controllers/drafts_controller.rb +++ b/app/controllers/drafts_controller.rb @@ -17,7 +17,15 @@ class DraftsController < ApplicationController limit: fetch_limit_from_params(default: nil, max: INDEX_LIMIT), ) - render json: { drafts: stream ? serialize_data(stream, DraftSerializer) : [] } + response = { drafts: serialize_data(stream, DraftSerializer) } + + if guardian.can_lazy_load_categories? + category_ids = stream.map { |draft| draft.topic&.category_id }.compact.uniq + categories = Category.secured(guardian).with_parents(category_ids) + response[:categories] = serialize_data(categories, CategoryBadgeSerializer) + end + + render json: response end def show diff --git a/app/controllers/user_actions_controller.rb b/app/controllers/user_actions_controller.rb index a166bca6f5b..ebad42936c4 100644 --- a/app/controllers/user_actions_controller.rb +++ b/app/controllers/user_actions_controller.rb @@ -28,7 +28,16 @@ class UserActionsController < ApplicationController } stream = UserAction.stream(opts).to_a - render_serialized(stream, UserActionSerializer, root: "user_actions") + + response = { user_actions: serialize_data(stream, UserActionSerializer) } + + if guardian.can_lazy_load_categories? + category_ids = stream.map(&:category_id).compact.uniq + categories = Category.secured(guardian).with_parents(category_ids) + response[:categories] = serialize_data(categories, CategoryBadgeSerializer) + end + + render json: response end def show diff --git a/app/models/category.rb b/app/models/category.rb index 1dae5366199..442da2653b2 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -211,6 +211,12 @@ class Category < ActiveRecord::Base ) SQL + scope :with_parents, ->(ids) { where(<<~SQL, ids: ids) } + id IN (:ids) + OR + id IN (SELECT DISTINCT parent_category_id FROM categories WHERE id IN (:ids)) + SQL + delegate :post_template, to: "self.class" # permission is just used by serialization diff --git a/app/models/post_revision.rb b/app/models/post_revision.rb index 5fd4ec0b40f..7422726fec1 100644 --- a/app/models/post_revision.rb +++ b/app/models/post_revision.rb @@ -31,7 +31,7 @@ class PostRevision < ActiveRecord::Base def categories return [] if modifications["category_id"].blank? - @categories ||= Category.where(id: modifications["category_id"]) + @categories ||= Category.with_parents(modifications["category_id"]) end def hide! diff --git a/app/serializers/about_serializer.rb b/app/serializers/about_serializer.rb index 68033acbd66..27b15d69a09 100644 --- a/app/serializers/about_serializer.rb +++ b/app/serializers/about_serializer.rb @@ -1,16 +1,12 @@ # frozen_string_literal: true class AboutSerializer < ApplicationSerializer - class CategoryAboutSerializer < ApplicationSerializer - attributes :id, :name, :color, :slug, :parent_category_id - end - class UserAboutSerializer < BasicUserSerializer attributes :title, :last_seen_at end class AboutCategoryModsSerializer < ApplicationSerializer - has_one :category, serializer: CategoryAboutSerializer, embed: :objects + has_one :category, serializer: CategoryBadgeSerializer, embed: :objects has_many :moderators, serializer: UserAboutSerializer, embed: :objects end diff --git a/app/serializers/category_badge_serializer.rb b/app/serializers/category_badge_serializer.rb new file mode 100644 index 00000000000..ec564f4cc80 --- /dev/null +++ b/app/serializers/category_badge_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class CategoryBadgeSerializer < ApplicationSerializer + attributes :id, :name, :color, :slug, :parent_category_id +end diff --git a/app/serializers/post_revision_serializer.rb b/app/serializers/post_revision_serializer.rb index 3f9021f56b3..a1a6698e958 100644 --- a/app/serializers/post_revision_serializer.rb +++ b/app/serializers/post_revision_serializer.rb @@ -28,7 +28,7 @@ class PostRevisionSerializer < ApplicationSerializer :wiki, :can_edit - has_many :categories, serializer: BasicCategorySerializer, embed: :objects + has_many :categories, serializer: CategoryBadgeSerializer, embed: :objects # Creates a field called field_name_changes with previous and # current members if a field has changed in this revision diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index f32ea81afa9..cdd1b4372de 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -223,6 +223,19 @@ RSpec.describe Category do end end + describe "with_parents" do + fab!(:category) + fab!(:subcategory) { Fabricate(:category, parent_category: category) } + + it "returns parent categories and subcategories" do + expect(Category.with_parents([category.id])).to contain_exactly(category) + end + + it "returns only categories if top-level categories" do + expect(Category.with_parents([subcategory.id])).to contain_exactly(category, subcategory) + end + end + describe "security" do fab!(:category) { Fabricate(:category_with_definition) } fab!(:category_2) { Fabricate(:category_with_definition) } diff --git a/spec/requests/drafts_controller_spec.rb b/spec/requests/drafts_controller_spec.rb index af81ab1b14b..40b16dedcf1 100644 --- a/spec/requests/drafts_controller_spec.rb +++ b/spec/requests/drafts_controller_spec.rb @@ -50,6 +50,21 @@ RSpec.describe DraftsController do expect(response.status).to eq(200) expect(response.parsed_body["drafts"].first["title"]).to eq(nil) end + + it "returns categories when lazy load categories is enabled" do + SiteSetting.lazy_load_categories_groups = "#{Group::AUTO_GROUPS[:everyone]}" + category = Fabricate(:category) + topic = Fabricate(:topic, category: category) + Draft.set(topic.user, "topic_#{topic.id}", 0, "{}") + sign_in(topic.user) + + get "/drafts.json" + expect(response.status).to eq(200) + draft_keys = response.parsed_body["drafts"].map { |draft| draft["draft_key"] } + expect(draft_keys).to contain_exactly("topic_#{topic.id}") + category_ids = response.parsed_body["categories"].map { |cat| cat["id"] } + expect(category_ids).to contain_exactly(category.id) + end end describe "#show" do diff --git a/spec/requests/user_actions_controller_spec.rb b/spec/requests/user_actions_controller_spec.rb index 2ee10780f42..71dc113628f 100644 --- a/spec/requests/user_actions_controller_spec.rb +++ b/spec/requests/user_actions_controller_spec.rb @@ -28,6 +28,14 @@ RSpec.describe UserActionsController do expect(actions.first).not_to include "email" end + it "returns categories when lazy load categories is enabled" do + SiteSetting.lazy_load_categories_groups = "#{Group::AUTO_GROUPS[:everyone]}" + user_actions + expect(response.status).to eq(200) + category_ids = response.parsed_body["categories"].map { |category| category["id"] } + expect(category_ids).to contain_exactly(post.topic.category.id) + end + context "when 'acting_username' is provided" do let(:user) { Fabricate(:user) }