# frozen_string_literal: true class TopicList include ActiveModel::Serialization cattr_accessor :preloaded_custom_fields self.preloaded_custom_fields = Set.new def self.on_preload(&blk) (@preload ||= Set.new) << blk end def self.cancel_preload(&blk) if @preload @preload.delete blk @preload = nil if @preload.length == 0 end end def self.preload(topics, object) @preload.each { |preload| preload.call(topics, object) } if @preload end def self.on_preload_user_ids(&blk) (@preload_user_ids ||= Set.new) << blk end def self.preload_user_ids(topics, user_ids, object) if @preload_user_ids @preload_user_ids.each do |preload_user_ids| user_ids = preload_user_ids.call(topics, user_ids, object) end end user_ids end attr_accessor( :more_topics_url, :prev_topics_url, :filter, :for_period, :per_page, :current_user, :tags, :shared_drafts, :category, :publish_read_state, ) def initialize(filter, current_user, topics, opts = nil) @filter = filter @current_user = current_user @topics_input = topics @opts = opts || {} @category = Category.find_by(id: @opts[:category_id]) if @opts[:category] @tags = Tag.where(id: @opts[:tag_ids]).all if @opts[:tag_ids].present? @publish_read_state = !!@opts[:publish_read_state] end def top_tags opts = @category ? { category: @category } : {} opts[:guardian] = Guardian.new(@current_user) Tag.top_tags(**opts) end def preload_key "topic_list" end # Lazy initialization def topics @topics ||= load_topics end def categories @categories ||= topics.map { |t| [t.category&.parent_category, t.category] }.flatten.uniq.compact end def load_topics @topics = @topics_input # Attach some data for serialization to each topic @topic_lookup = TopicUser.lookup_for(@current_user, @topics) if @current_user @dismissed_topic_users_lookup = DismissedTopicUser.lookup_for(@current_user, @topics) if @current_user post_action_type = if @current_user if @opts[:filter].present? PostActionType.types[:like] if @opts[:filter] == "liked" end end # Data for bookmarks or likes post_action_lookup = PostAction.lookup_for(@current_user, @topics, post_action_type) if post_action_type # Create a lookup for all the user ids we need user_ids = [] group_ids = [] @topics.each do |ft| user_ids << ft.user_id << ft.last_post_user_id << ft.featured_user_ids << ft.allowed_user_ids group_ids |= (ft.allowed_group_ids || []) end user_ids = TopicList.preload_user_ids(@topics, user_ids, self) user_lookup = UserLookup.new(user_ids) group_lookup = GroupLookup.new(group_ids) @topics.each do |ft| ft.user_data = @topic_lookup[ft.id] if @topic_lookup.present? if ft.regular? && category_user_lookup.present? ft.category_user_data = @category_user_lookup[ft.category_id] end ft.dismissed = @current_user && @dismissed_topic_users_lookup.include?(ft.id) if ft.user_data && post_action_lookup && actions = post_action_lookup[ft.id] ft.user_data.post_action_data = { post_action_type => actions } end ft.posters = ft.posters_summary(user_lookup: user_lookup) ft.participants = ft.participants_summary(user_lookup: user_lookup, user: @current_user) ft.participant_groups = ft.participant_groups_summary(group_lookup: group_lookup, group: @opts[:group]) ft.topic_list = self end 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( records: @topics, associations: topic_preloader_associations, ).call if preloaded_custom_fields.present? Topic.preload_custom_fields(@topics, preloaded_custom_fields) end TopicList.preload(@topics, self) @topics end def attributes { "more_topics_url" => page } end private def category_user_lookup @category_user_lookup ||= begin if @current_user CategoryUser.lookup_for(@current_user, @topics.map(&:category_id).uniq) else [] end end end end