diff --git a/app/assets/javascripts/discourse/components/latest-topic-list.js.es6 b/app/assets/javascripts/discourse/components/categories-and-top-topics.js.es6
similarity index 54%
rename from app/assets/javascripts/discourse/components/latest-topic-list.js.es6
rename to app/assets/javascripts/discourse/components/categories-and-top-topics.js.es6
index 664eb94c31e..4f1c8886929 100644
--- a/app/assets/javascripts/discourse/components/latest-topic-list.js.es6
+++ b/app/assets/javascripts/discourse/components/categories-and-top-topics.js.es6
@@ -1,3 +1,3 @@
export default Ember.Component.extend({
- classNames: ['latest-topic-list']
+ classNames: ["categories-and-top"]
});
diff --git a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6 b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
index 0ba2194e501..9753a776eb6 100644
--- a/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
+++ b/app/assets/javascripts/discourse/routes/discovery-categories.js.es6
@@ -12,20 +12,24 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
this.render("discovery/categories", { outlet: "list-container" });
},
- model() {
- const style = !this.site.mobileView && this.siteSettings.desktop_category_page_style;
- const parentCategory = this.get("model.parentCategory");
+ findCategories() {
+ let style = !this.site.mobileView &&
+ this.siteSettings.desktop_category_page_style;
- let promise;
+ let parentCategory = this.get("model.parentCategory");
if (parentCategory) {
- promise = CategoryList.listForParent(this.store, parentCategory);
+ return CategoryList.listForParent(this.store, parentCategory);
} else if (style === "categories_and_latest_topics") {
- promise = this._loadCategoriesAndLatestTopics();
- } else {
- promise = CategoryList.list(this.store);
+ return this._findCategoriesAndTopics('latest');
+ } else if (style === "categories_and_top_topics") {
+ return this._findCategoriesAndTopics('top');
}
- return promise.then(model => {
+ return CategoryList.list(this.store);
+ },
+
+ model() {
+ return this.findCategories().then(model => {
const tracking = this.topicTrackingState;
if (tracking) {
tracking.sync(model, "categories");
@@ -35,26 +39,31 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
});
},
- _loadCategoriesAndLatestTopics() {
- const wrappedCategoriesList = PreloadStore.getAndRemove("categories_list");
- const topicListLatest = PreloadStore.getAndRemove("topic_list_latest");
- const categoriesList = wrappedCategoriesList && wrappedCategoriesList.category_list;
- if (categoriesList && topicListLatest) {
- return new Ember.RSVP.Promise(resolve => {
- const result = Ember.Object.create({
- categories: CategoryList.categoriesFrom(this.store, wrappedCategoriesList),
- topics: TopicList.topicsFrom(this.store, topicListLatest),
+ _findCategoriesAndTopics(filter) {
+ return Ember.RSVP.hash({
+ wrappedCategoriesList: PreloadStore.getAndRemove("categories_list"),
+ topicsList: PreloadStore.getAndRemove(`topic_list_${filter}`)
+ }).then(hash => {
+ let { wrappedCategoriesList, topicsList } = hash;
+ let categoriesList = wrappedCategoriesList &&
+ wrappedCategoriesList.category_list;
+
+ if (categoriesList && topicsList) {
+ return Ember.Object.create({
+ categories: CategoryList.categoriesFrom(
+ this.store,
+ wrappedCategoriesList
+ ),
+ topics: TopicList.topicsFrom(this.store, topicsList),
can_create_category: categoriesList.can_create_category,
can_create_topic: categoriesList.can_create_topic,
draft_key: categoriesList.draft_key,
draft: categoriesList.draft,
draft_sequence: categoriesList.draft_sequence
});
-
- resolve(result);
- });
- } else {
- return ajax("/categories_and_latest").then(result => {
+ }
+ // Otherwise, return the ajax result
+ return ajax(`/categories_and_${filter}`).then(result => {
return Ember.Object.create({
categories: CategoryList.categoriesFrom(this.store, result),
topics: TopicList.topicsFrom(this.store, result),
@@ -65,7 +74,7 @@ const DiscoveryCategoriesRoute = Discourse.Route.extend(OpenComposer, {
draft_sequence: result.category_list.draft_sequence
});
});
- }
+ });
},
titleToken() {
diff --git a/app/assets/javascripts/discourse/templates/components/categories-and-latest-topics.hbs b/app/assets/javascripts/discourse/templates/components/categories-and-latest-topics.hbs
index 1a32cea92d3..be7ee3e4a31 100644
--- a/app/assets/javascripts/discourse/templates/components/categories-and-latest-topics.hbs
+++ b/app/assets/javascripts/discourse/templates/components/categories-and-latest-topics.hbs
@@ -3,5 +3,5 @@
- {{latest-topic-list topics=topics}}
+ {{categories-topic-list topics=topics filter="latest" class="latest-topic-list"}}
diff --git a/app/assets/javascripts/discourse/templates/components/categories-and-top-topics.hbs b/app/assets/javascripts/discourse/templates/components/categories-and-top-topics.hbs
new file mode 100644
index 00000000000..4e84c4d6318
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/categories-and-top-topics.hbs
@@ -0,0 +1,7 @@
+
+ {{categories-only categories=categories}}
+
+
+
+ {{categories-topic-list topics=topics filter="top" class="top-topic-list"}}
+
diff --git a/app/assets/javascripts/discourse/templates/components/categories-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/categories-topic-list.hbs
new file mode 100644
index 00000000000..7761050a38a
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/categories-topic-list.hbs
@@ -0,0 +1,16 @@
+
+ {{i18n (concat "filters." filter ".title")}}
+
+
+{{#if topics}}
+ {{#each topics as |t|}}
+ {{latest-topic-list-item topic=t}}
+ {{/each}}
+
+{{else}}
+
+
{{i18n (concat "topics.none." filter)}}
+
+{{/if}}
diff --git a/app/assets/javascripts/discourse/templates/components/latest-topic-list-contents.hbs b/app/assets/javascripts/discourse/templates/components/latest-topic-list-contents.hbs
deleted file mode 100644
index 33b8ec95b90..00000000000
--- a/app/assets/javascripts/discourse/templates/components/latest-topic-list-contents.hbs
+++ /dev/null
@@ -1,3 +0,0 @@
-{{#each topics as |t|}}
- {{latest-topic-list-item topic=t}}
-{{/each}}
diff --git a/app/assets/javascripts/discourse/templates/components/latest-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/latest-topic-list.hbs
deleted file mode 100644
index 4b339c3f3f0..00000000000
--- a/app/assets/javascripts/discourse/templates/components/latest-topic-list.hbs
+++ /dev/null
@@ -1,14 +0,0 @@
-
- {{i18n "filters.latest.title"}}
-
-
-{{#if topics}}
- {{latest-topic-list-contents topics=topics tagName=""}}
-
-{{else}}
-
-
{{i18n "topics.none.latest"}}
-
-{{/if}}
diff --git a/app/assets/stylesheets/desktop/category-list.scss b/app/assets/stylesheets/desktop/category-list.scss
index 9eb594f9e5a..f3263e96616 100644
--- a/app/assets/stylesheets/desktop/category-list.scss
+++ b/app/assets/stylesheets/desktop/category-list.scss
@@ -110,7 +110,7 @@
}
}
-.categories-and-latest {
+.categories-and-latest, .categories-and-top {
display: flex;
flex-flow: row wrap;
diff --git a/app/assets/stylesheets/desktop/latest-topic-list.scss b/app/assets/stylesheets/desktop/latest-topic-list.scss
index 16cbb60b6b0..6cdb77df6b0 100644
--- a/app/assets/stylesheets/desktop/latest-topic-list.scss
+++ b/app/assets/stylesheets/desktop/latest-topic-list.scss
@@ -1,4 +1,4 @@
-.latest-topic-list {
+.latest-topic-list, .top-topic-list {
@extend .topic-list-icons;
.table-heading {
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
index 9c95a1da071..62163e2a174 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -2,11 +2,11 @@ require_dependency 'category_serializer'
class CategoriesController < ApplicationController
- requires_login except: [:index, :categories_and_latest, :show, :redirect, :find_by_slug]
+ requires_login except: [:index, :categories_and_latest, :categories_and_top, :show, :redirect, :find_by_slug]
before_action :fetch_category, only: [:show, :update, :destroy]
before_action :initialize_staff_action_logger, only: [:create, :update, :destroy]
- skip_before_action :check_xhr, only: [:index, :categories_and_latest, :redirect]
+ skip_before_action :check_xhr, only: [:index, :categories_and_latest, :categories_and_top, :redirect]
def redirect
redirect_to path("/c/#{params[:path]}")
@@ -27,7 +27,10 @@ class CategoriesController < ApplicationController
@category_list = CategoryList.new(guardian, category_options)
@category_list.draft_key = Draft::NEW_TOPIC
- @category_list.draft_sequence = DraftSequence.current(current_user, Draft::NEW_TOPIC)
+ @category_list.draft_sequence = DraftSequence.current(
+ current_user,
+ Draft::NEW_TOPIC
+ )
@category_list.draft = Draft.get(current_user, Draft::NEW_TOPIC, @category_list.draft_sequence) if current_user
@title = "#{I18n.t('js.filters.categories.title')} - #{SiteSetting.title}" unless category_options[:is_homepage]
@@ -36,10 +39,23 @@ class CategoriesController < ApplicationController
format.html do
store_preloaded(@category_list.preload_key, MultiJson.dump(CategoryListSerializer.new(@category_list, scope: guardian)))
- if SiteSetting.desktop_category_page_style == "categories_and_latest_topics".freeze
- topic_options = { per_page: SiteSetting.categories_topics, no_definitions: true }
- topic_list = TopicQuery.new(current_user, topic_options).list_latest
- store_preloaded(topic_list.preload_key, MultiJson.dump(TopicListSerializer.new(topic_list, scope: guardian)))
+ style = SiteSetting.desktop_category_page_style
+ topic_options = {
+ per_page: SiteSetting.categories_topics,
+ no_definitions: true
+ }
+
+ if style == "categories_and_latest_topics".freeze
+ @topic_list = TopicQuery.new(current_user, topic_options).list_latest
+ elsif style == "categories_and_top_topics".freeze
+ @topic_list = TopicQuery.new(nil, topic_options).list_top_for(SiteSetting.top_page_default_timeframe.to_sym)
+ end
+
+ if @topic_list.present?
+ store_preloaded(
+ @topic_list.preload_key,
+ MultiJson.dump(TopicListSerializer.new(@topic_list, scope: guardian))
+ )
end
render
@@ -50,35 +66,11 @@ class CategoriesController < ApplicationController
end
def categories_and_latest
- discourse_expires_in 1.minute
+ categories_and_topics(:latest)
+ end
- category_options = {
- is_homepage: current_homepage == "categories".freeze,
- parent_category_id: params[:parent_category_id],
- include_topics: false
- }
-
- topic_options = {
- per_page: SiteSetting.categories_topics,
- no_definitions: true,
- exclude_category_ids: Category.where(suppress_from_latest: true).pluck(:id)
- }
-
- result = CategoryAndTopicLists.new
- result.category_list = CategoryList.new(guardian, category_options)
- result.topic_list = TopicQuery.new(current_user, topic_options).list_latest
-
- draft_key = Draft::NEW_TOPIC
- draft_sequence = DraftSequence.current(current_user, draft_key)
- draft = Draft.get(current_user, draft_key, draft_sequence) if current_user
-
- %w{category topic}.each do |type|
- result.send(:"#{type}_list").draft = draft
- result.send(:"#{type}_list").draft_key = draft_key
- result.send(:"#{type}_list").draft_sequence = draft_sequence
- end
-
- render_serialized(result, CategoryAndTopicListsSerializer, root: false)
+ def categories_and_top
+ categories_and_topics(:top)
end
def move
@@ -208,6 +200,43 @@ class CategoriesController < ApplicationController
end
private
+ def categories_and_topics(topics_filter)
+ discourse_expires_in 1.minute
+
+ category_options = {
+ is_homepage: current_homepage == "categories".freeze,
+ parent_category_id: params[:parent_category_id],
+ include_topics: false
+ }
+
+ topic_options = {
+ per_page: SiteSetting.categories_topics,
+ no_definitions: true,
+ exclude_category_ids: Category.where(suppress_from_latest: true).pluck(:id)
+ }
+
+ result = CategoryAndTopicLists.new
+ result.category_list = CategoryList.new(guardian, category_options)
+
+ if topics_filter == :latest
+ result.topic_list = TopicQuery.new(current_user, topic_options).list_latest
+ elsif topics_filter == :top
+ result.topic_list = TopicQuery.new(nil, topic_options).list_top_for(SiteSetting.top_page_default_timeframe.to_sym)
+ end
+
+ draft_key = Draft::NEW_TOPIC
+ draft_sequence = DraftSequence.current(current_user, draft_key)
+ draft = Draft.get(current_user, draft_key, draft_sequence) if current_user
+
+ %w{category topic}.each do |type|
+ result.send(:"#{type}_list").draft = draft
+ result.send(:"#{type}_list").draft_key = draft_key
+ result.send(:"#{type}_list").draft_sequence = draft_sequence
+ end
+
+ render_serialized(result, CategoryAndTopicListsSerializer, root: false)
+ end
+
def required_param_keys
[:name, :color, :text_color]
@@ -269,9 +298,11 @@ class CategoriesController < ApplicationController
end
def include_topics(parent_category = nil)
+ style = SiteSetting.desktop_category_page_style
view_context.mobile_view? ||
- params[:include_topics] ||
- (parent_category && parent_category.subcategory_list_includes_topics?) ||
- SiteSetting.desktop_category_page_style == "categories_with_featured_topics".freeze
+ params[:include_topics] ||
+ (parent_category && parent_category.subcategory_list_includes_topics?) ||
+ style == "categories_with_featured_topics".freeze ||
+ style == "categories_with_top_topics".freeze
end
end
diff --git a/app/models/category_page_style.rb b/app/models/category_page_style.rb
index fe82ea95db0..d45376ad1f1 100644
--- a/app/models/category_page_style.rb
+++ b/app/models/category_page_style.rb
@@ -11,6 +11,7 @@ class CategoryPageStyle < EnumSiteSetting
{ name: 'category_page_style.categories_only', value: 'categories_only' },
{ name: 'category_page_style.categories_with_featured_topics', value: 'categories_with_featured_topics' },
{ name: 'category_page_style.categories_and_latest_topics', value: 'categories_and_latest_topics' },
+ { name: 'category_page_style.categories_and_top_topics', value: 'categories_and_top_topics' },
]
end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 7a3f86ab745..65ebd21d03c 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1202,6 +1202,7 @@ en:
categories_only: "Categories Only"
categories_with_featured_topics: "Categories with Featured Topics"
categories_and_latest_topics: "Categories and Latest Topics"
+ categories_and_top_topics: "Categories and Top Topics"
shortcut_modifier_key:
shift: 'Shift'
diff --git a/config/routes.rb b/config/routes.rb
index ed289687e3c..de27e9079aa 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -542,6 +542,7 @@ Discourse::Application.routes.draw do
put "category/:category_id/slug" => "categories#update_slug"
get "categories_and_latest" => "categories#categories_and_latest"
+ get "categories_and_top" => "categories#categories_and_top"
get "c/:id/show" => "categories#show"
get "c/:category_slug/find_by_slug" => "categories#find_by_slug"
diff --git a/spec/requests/list_controller_spec.rb b/spec/requests/list_controller_spec.rb
index abe82cea777..e7a92f1f8be 100644
--- a/spec/requests/list_controller_spec.rb
+++ b/spec/requests/list_controller_spec.rb
@@ -15,6 +15,21 @@ RSpec.describe ListController do
end
end
+ describe "categories and X" do
+ it "returns top topics" do
+ Fabricate(:topic, like_count: 1000, posts_count: 100)
+ TopTopic.refresh!
+
+ get "/categories_and_top.json"
+ data = JSON.parse(response.body)
+ expect(data["topic_list"]["topics"].length).to eq(1)
+
+ get "/categories_and_latest.json"
+ data = JSON.parse(response.body)
+ expect(data["topic_list"]["topics"].length).to eq(1)
+ end
+ end
+
describe 'suppress from latest' do
it 'supresses categories' do