UX: Introduce automatic 'categories topics' setting (#8804)

When 'categories topics' setting is set to 0, the system will
automatically try to find a value to keep the two columns (categories
and topics) symmetrical.

The value is computed as 1.5x the number of top level categories and at
least 5 topics will always be returned.
This commit is contained in:
Dan Ungureanu 2020-01-29 20:30:48 +02:00 committed by GitHub
parent d24d47b2ee
commit 09e8be3209
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 5 deletions

View File

@ -8,6 +8,9 @@ class CategoriesController < ApplicationController
before_action :initialize_staff_action_logger, only: [:create, :update, :destroy]
skip_before_action :check_xhr, only: [:index, :categories_and_latest, :categories_and_top, :redirect]
SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR = 1.5
MIN_CATEGORIES_TOPICS = 5
def redirect
return if handle_permalink("/category/#{params[:path]}")
redirect_to path("/c/#{params[:path]}")
@ -46,7 +49,7 @@ class CategoriesController < ApplicationController
style = SiteSetting.desktop_category_page_style
topic_options = {
per_page: SiteSetting.categories_topics,
per_page: CategoriesController.topics_per_page,
no_definitions: true
}
@ -226,6 +229,15 @@ class CategoriesController < ApplicationController
end
private
def self.topics_per_page
return SiteSetting.categories_topics if SiteSetting.categories_topics > 0
count = Category.where(parent_category: nil).count
count = (SYMMETRICAL_CATEGORIES_TO_TOPICS_FACTOR * count).to_i
count > MIN_CATEGORIES_TOPICS ? count : MIN_CATEGORIES_TOPICS
end
def categories_and_topics(topics_filter)
discourse_expires_in 1.minute
@ -236,7 +248,7 @@ class CategoriesController < ApplicationController
}
topic_options = {
per_page: SiteSetting.categories_topics,
per_page: CategoriesController.topics_per_page,
no_definitions: true
}

View File

@ -1686,7 +1686,7 @@ en:
alert_admins_if_errors_per_minute: "Number of errors per minute in order to trigger an admin alert. A value of 0 disables this feature. NOTE: requires restart."
alert_admins_if_errors_per_hour: "Number of errors per hour in order to trigger an admin alert. A value of 0 disables this feature. NOTE: requires restart."
categories_topics: "Number of topics to show in /categories page."
categories_topics: "Number of topics to show in /categories page. If set to 0, it will automatically try to find a value to keep the two columns symmetrical (categories and topics)."
suggested_topics: "Number of suggested topics shown at the bottom of a topic."
limit_suggested_to_category: "Only show topics from the current category in suggested topics."
suggested_topics_max_days_old: "Suggested topics should not be more than n days old."

View File

@ -135,8 +135,7 @@ basic:
default: false
categories_topics:
default: 20
min: 5
max: 2000
validator: "CategoriesTopicsValidator"
suggested_topics:
client: true
default: 5

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class CategoriesTopicsValidator
def initialize(opts = {})
@opts = opts
end
def valid_value?(val)
num = val.to_i
return false if num.to_s != val.to_s
return false if num != 0 && num < CategoriesController::MIN_CATEGORIES_TOPICS
true
end
def error_message
I18n.t('site_settings.errors.invalid_integer_min', min: 5)
end
end

View File

@ -481,4 +481,41 @@ describe CategoriesController do
end
end
end
context '#categories_and_topics' do
before do
10.times.each { Fabricate(:topic) }
end
it 'works when SiteSetting.categories_topics is non-null' do
SiteSetting.categories_topics = 5
get '/categories_and_latest.json'
expect(JSON.parse(response.body)['topic_list']['topics'].size).to eq(5)
end
it 'works when SiteSetting.categories_topics is null' do
SiteSetting.categories_topics = 0
get '/categories_and_latest.json'
json = JSON.parse(response.body)
expect(json['category_list']['categories'].size).to eq(2) # 'Uncategorized' and category
expect(json['topic_list']['topics'].size).to eq(5)
Fabricate(:category, parent_category: category)
get '/categories_and_latest.json'
json = JSON.parse(response.body)
expect(json['category_list']['categories'].size).to eq(2)
expect(json['topic_list']['topics'].size).to eq(5)
Fabricate(:category)
Fabricate(:category)
get '/categories_and_latest.json'
json = JSON.parse(response.body)
expect(json['category_list']['categories'].size).to eq(4)
expect(json['topic_list']['topics'].size).to eq(6)
end
end
end