FEATURE: Async load of category and chat hashtags (#25526)

This commit includes several changes to make hashtags work when "lazy
load categories" is enabled. The previous hashtag implementation use the
category colors CSS variables, but these are not defined when the site
setting is enabled because categories are no longer preloaded.

This commit implements two fundamental changes:

1. load colors together with the other hashtag information

2. load cooked hashtag data asynchronously

The first change is implemented by adding "colors" to the HashtagItem
model. It is a list because two colors are returned for subcategories:
the color of the parent category and subcategory.

The second change is implemented on the server-side in a new route
/hashtags/by-ids and on the client side by loading previously unseen
hashtags, generating the CSS on the fly and injecting it into the page.

There have been minimal changes outside of these two fundamental ones,
but a refactoring will be coming soon to reuse as much of the code
and maybe favor use of `style` rather than injecting CSS into the page,
which can lead to page rerenders and indefinite grow of the styles.
This commit is contained in:
Bianca Nenciu
2024-02-12 12:07:14 +02:00
committed by GitHub
parent 6b596151ff
commit 1403217ca4
22 changed files with 420 additions and 67 deletions

View File

@@ -22,6 +22,7 @@ class CategoryHashtagDataSource
item.slug = category.slug
item.description = category.description_text
item.icon = icon
item.colors = [category.parent_category&.color, category.color].compact
item.relative_url = category.url
item.id = category.id
@@ -31,6 +32,10 @@ class CategoryHashtagDataSource
end
end
def self.find_by_ids(guardian, ids)
Category.secured(guardian).where(id: ids).map { |category| category_to_hashtag_item(category) }
end
def self.lookup(guardian, slugs)
user_categories =
Category
@@ -51,7 +56,7 @@ class CategoryHashtagDataSource
base_search =
Category
.secured(guardian)
.select(:id, :parent_category_id, :slug, :name, :description)
.select(:id, :parent_category_id, :slug, :name, :description, :color)
.includes(:parent_category)
if condition == HashtagAutocompleteService.search_conditions[:starts_with]

View File

@@ -85,6 +85,9 @@ class HashtagAutocompleteService
# The icon to display in the UI autocomplete menu for the item.
attr_accessor :icon
# The colors to use when displaying the symbol/icon for the hashtag, e.g. category badge
attr_accessor :colors
# Distinguishes between different entities e.g. tag, category.
attr_accessor :type
@@ -106,6 +109,7 @@ class HashtagAutocompleteService
@text = params[:text]
@description = params[:description]
@icon = params[:icon]
@colors = params[:colors]
@type = params[:type]
@ref = params[:ref]
@slug = params[:slug]
@@ -118,6 +122,7 @@ class HashtagAutocompleteService
text: self.text,
description: self.description,
icon: self.icon,
colors: self.colors,
type: self.type,
ref: self.ref,
slug: self.slug,
@@ -130,6 +135,22 @@ class HashtagAutocompleteService
@guardian = guardian
end
def find_by_ids(ids_by_type)
HashtagAutocompleteService
.data_source_types
.each_with_object({}) do |type, hash|
next if ids_by_type[type].blank?
data_source = HashtagAutocompleteService.data_source_from_type(type)
next if !data_source.respond_to?(:find_by_ids)
hashtags = data_source.find_by_ids(guardian, ids_by_type[type])
next if hashtags.blank?
hash[type] = set_types(hashtags, type).map(&:to_h)
end
end
##
# Finds resources of the provided types by their exact slugs, unlike
# search which can search partial names, slugs, etc. Used for cooking