From 3a6bffa05db4cbc7dd6106473cecf82e87f14085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Tue, 14 Jan 2014 01:02:14 +0100 Subject: [PATCH] FEATURE: better top pages --- app/assets/javascripts/discourse.js | 5 +- .../components/basic_topic_list_component.js | 10 ++- .../controllers/list_categories_controller.js | 2 +- .../controllers/list_controller.js.erb | 8 +- .../controllers/list_top_controller.js | 20 +++-- .../controllers/static_controller.js | 6 +- app/assets/javascripts/discourse/lib/url.js | 2 +- .../javascripts/discourse/models/category.js | 4 +- .../discourse/models/category_list.js | 29 ++----- .../discourse/models/nav_item.js.erb | 19 ++--- .../javascripts/discourse/models/top_list.js | 34 -------- .../discourse/models/top_list.js.erb | 37 +++++++++ .../discourse/models/topic_list.js | 3 +- .../javascripts/discourse/models/user.js | 2 - .../discourse/routes/application_routes.js | 82 +++++++++++-------- .../discourse/routes/discourse_route.js | 2 +- .../discourse/routes/filtered_list_route.js | 34 ++++---- .../discourse/routes/list_categories_route.js | 52 ++++++------ .../discourse/routes/list_category_route.js | 37 +++++---- .../discourse/routes/list_top_route.js | 44 ++++++---- .../discourse/routes/static_route.js | 8 +- .../routes/user_topic_list_routes.js | 5 +- .../components/basic-topic-list.js.handlebars | 2 + .../templates/list/categories.js.handlebars | 29 +++---- .../templates/list/top.js.handlebars | 48 +++++++++++ .../discourse/templates/top.js.handlebars | 18 ---- .../discourse/views/list/list_top_view.js | 18 ++++ .../discourse/views/nav_item_view.js | 2 +- .../stylesheets/desktop/topic-list.scss | 2 + app/controllers/list_controller.rb | 55 ++++++++++--- app/serializers/top_list_serializer.rb | 15 +--- config/locales/client.en.yml | 3 + config/routes.rb | 44 +++++----- .../20131223171005_create_top_topics.rb | 11 ++- lib/topic_query.rb | 8 +- test/javascripts/lib/url_test.js | 2 +- 36 files changed, 400 insertions(+), 302 deletions(-) delete mode 100644 app/assets/javascripts/discourse/models/top_list.js create mode 100644 app/assets/javascripts/discourse/models/top_list.js.erb create mode 100644 app/assets/javascripts/discourse/templates/list/top.js.handlebars delete mode 100644 app/assets/javascripts/discourse/templates/top.js.handlebars create mode 100644 app/assets/javascripts/discourse/views/list/list_top_view.js diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index 59abdb6336e..97f4f1a3136 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -88,9 +88,8 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { }, loginRequired: function() { - return ( - Discourse.SiteSettings.login_required && !Discourse.User.current() - ); + return Discourse.SiteSettings.login_required && + !Discourse.User.current(); }.property(), redirectIfLoginRequired: function(route) { diff --git a/app/assets/javascripts/discourse/components/basic_topic_list_component.js b/app/assets/javascripts/discourse/components/basic_topic_list_component.js index 41aada403f0..065383a068d 100644 --- a/app/assets/javascripts/discourse/components/basic_topic_list_component.js +++ b/app/assets/javascripts/discourse/components/basic_topic_list_component.js @@ -22,10 +22,12 @@ Discourse.BasicTopicListComponent = Ember.Component.extend({ }.observes('topicList'), _initFromTopicList: function(topicList) { - this.setProperties({ - topics: topicList.get('topics'), - sortOrder: topicList.get('sortOrder') - }); + if (topicList !== null) { + this.setProperties({ + topics: topicList.get('topics'), + sortOrder: topicList.get('sortOrder') + }); + } }, init: function() { diff --git a/app/assets/javascripts/discourse/controllers/list_categories_controller.js b/app/assets/javascripts/discourse/controllers/list_categories_controller.js index 2f285bc94c6..4e10b6f5af8 100644 --- a/app/assets/javascripts/discourse/controllers/list_categories_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_categories_controller.js @@ -30,7 +30,7 @@ Discourse.ListCategoriesController = Discourse.ObjectController.extend({ latestTopicOnly: function() { return this.get('categories').find(function(c) { return c.get('featuredTopics.length') > 1; }) === undefined; - }.property('categories.featuredTopics.length') + }.property('categories.@each.featuredTopics.length') }); diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js.erb b/app/assets/javascripts/discourse/controllers/list_controller.js.erb index e47473f2152..9361cb74a8a 100644 --- a/app/assets/javascripts/discourse/controllers/list_controller.js.erb +++ b/app/assets/javascripts/discourse/controllers/list_controller.js.erb @@ -95,12 +95,12 @@ Discourse.ListController = Discourse.Controller.extend({ // Put in the appropriate page title based on our view updateTitle: function() { if (this.get('filterMode') === 'categories') { - return Discourse.set('title', I18n.t('categories_list')); + Discourse.set('title', I18n.t('categories_list')); } else { if (this.present('category')) { - return Discourse.set('title', this.get('category.name').capitalize() + " " + I18n.t('topic.list')); + Discourse.set('title', this.get('category.name').capitalize() + " " + I18n.t('topic.list')); } else { - return Discourse.set('title', I18n.t('topic.list')); + Discourse.set('title', I18n.t('topic.list')); } } }.observes('filterMode', 'category'), @@ -134,5 +134,5 @@ Discourse.ListController = Discourse.Controller.extend({ }); Discourse.ListController.reopenClass({ - filters: <%= Discourse.filters.map(&:to_s) %> + FILTERS: <%= Discourse.filters.map(&:to_s) %> }); diff --git a/app/assets/javascripts/discourse/controllers/list_top_controller.js b/app/assets/javascripts/discourse/controllers/list_top_controller.js index cc0b6d0a7c5..f71e0bfdad7 100644 --- a/app/assets/javascripts/discourse/controllers/list_top_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_top_controller.js @@ -23,12 +23,18 @@ Discourse.ListTopController = Discourse.ObjectController.extend({ return null; }.property(), - showThisYear: function() { - if (Discourse.User.current()) { - return !Discourse.User.currentProp("hasBeenSeenInTheLastMonth"); - } else { - return true; - } - }.property() + hasDisplayedAllTopLists: Em.computed.and('content.yearly', 'content.monthly', 'content.weekly', 'content.daily'), + + showMoreUrl: function(period) { + var url = "", category = this.get("category"); + if (category) { url += category.get("url") + "/l"; } + url += "/top/" + period; + return url; + }, + + showMoreDailyUrl: function() { return this.showMoreUrl("daily"); }.property("category.url"), + showMoreWeeklyUrl: function() { return this.showMoreUrl("weekly"); }.property("category.url"), + showMoreMonthlyUrl: function() { return this.showMoreUrl("monthly"); }.property("category.url"), + showMoreYearlyUrl: function() { return this.showMoreUrl("yearly"); }.property("category.url"), }); diff --git a/app/assets/javascripts/discourse/controllers/static_controller.js b/app/assets/javascripts/discourse/controllers/static_controller.js index f3f945b8306..05b6d5c8ce5 100644 --- a/app/assets/javascripts/discourse/controllers/static_controller.js +++ b/app/assets/javascripts/discourse/controllers/static_controller.js @@ -37,12 +37,10 @@ Discourse.StaticController = Discourse.Controller.extend({ }); Discourse.StaticController.reopenClass({ - pages: ['faq', 'tos', 'privacy', 'login'], - configs: { + PAGES: ['faq', 'tos', 'privacy', 'login'], + CONFIGS: { 'faq': 'faq_url', 'tos': 'tos_url', 'privacy': 'privacy_policy_url' } }); - - diff --git a/app/assets/javascripts/discourse/lib/url.js b/app/assets/javascripts/discourse/lib/url.js index 0303d947255..ca78faba912 100644 --- a/app/assets/javascripts/discourse/lib/url.js +++ b/app/assets/javascripts/discourse/lib/url.js @@ -165,7 +165,7 @@ Discourse.URL = Em.Object.createWithMixins({ @param {String} path the path we're navigating to **/ navigatedToHome: function(oldPath, path) { - var defaultFilter = "/" + Discourse.ListController.filters[0]; + var defaultFilter = "/" + Discourse.ListController.FILTERS[0]; if (path === "/" && (oldPath === "/" || oldPath === defaultFilter)) { // Refresh our list diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index 2dae55ed8e2..6710c995aa4 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -44,7 +44,7 @@ Discourse.Category = Discourse.Model.extend({ }.property('url'), style: function() { - return "background-color: #" + this.get('category.color') + "; color: #" + (this.get('category.text_color')) + ";"; + return "background-color: #" + this.get('category.color') + "; color: #" + this.get('category.text_color') + ";"; }.property('color', 'text_color'), moreTopics: function() { @@ -54,7 +54,7 @@ Discourse.Category = Discourse.Model.extend({ save: function() { var url = "/categories"; if (this.get('id')) { - url = "/categories/" + (this.get('id')); + url = "/categories/" + this.get('id'); } return Discourse.ajax(url, { diff --git a/app/assets/javascripts/discourse/models/category_list.js b/app/assets/javascripts/discourse/models/category_list.js index 4428705858e..69ac544513d 100644 --- a/app/assets/javascripts/discourse/models/category_list.js +++ b/app/assets/javascripts/discourse/models/category_list.js @@ -16,7 +16,7 @@ Discourse.CategoryList = Ember.ArrayProxy.extend({ moveCategory: function(categoryId, position){ Discourse.ajax("/category/" + categoryId + "/move", { type: 'POST', - data: {position: position} + data: { position: position } }); } }); @@ -55,31 +55,20 @@ Discourse.CategoryList.reopenClass({ return categories; }, - list: function(filter) { - var self = this, - finder = null; + list: function() { + var self = this; - if (filter === 'categories') { - finder = PreloadStore.getAndRemove("categories_list", function() { - return Discourse.ajax("/categories.json"); - }); - } else { - finder = Discourse.ajax("/" + filter + ".json"); - } - - return finder.then(function(result) { - var categoryList = Discourse.TopicList.create(); - categoryList.setProperties({ + return PreloadStore.getAndRemove("categories_list", function() { + return Discourse.ajax("/categories.json"); + }).then(function(result) { + return Discourse.CategoryList.create({ + categories: self.categoriesFrom(result), can_create_category: result.category_list.can_create_category, can_create_topic: result.category_list.can_create_topic, - categories: self.categoriesFrom(result), draft_key: result.category_list.draft_key, - draft_sequence: result.category_list.draft_sequence + draft_sequence: result.category_list.draft_sequence, }); - return categoryList; }); } }); - - diff --git a/app/assets/javascripts/discourse/models/nav_item.js.erb b/app/assets/javascripts/discourse/models/nav_item.js.erb index 6e61c8d7721..09721d6e733 100644 --- a/app/assets/javascripts/discourse/models/nav_item.js.erb +++ b/app/assets/javascripts/discourse/models/nav_item.js.erb @@ -6,8 +6,6 @@ @namespace Discourse @module Discourse **/ -var validNavNames = <%= Discourse.top_menu_items.map(&:to_s) %>; -var validAnon = <%= Discourse.anonymous_top_menu_items.map(&:to_s) %>; Discourse.NavItem = Discourse.Model.extend({ @@ -69,26 +67,25 @@ Discourse.NavItem = Discourse.Model.extend({ }); Discourse.NavItem.reopenClass({ + NAMES: <%= Discourse.top_menu_items.map(&:to_s) %>, + ANONYMOUS_NAMES: <%= Discourse.anonymous_top_menu_items.map(&:to_s) %>, // create a nav item from the text, will return null if there is not valid nav item for this particular text fromText: function(text, opts) { - var countSummary = opts.countSummary, - split = text.split(","), + var split = text.split(","), name = split[0], testName = name.split("/")[0]; - if (!opts.loggedOn && !validAnon.contains(testName)) return null; + if (!opts.loggedOn && !Discourse.NavItem.ANONYMOUS_NAMES.contains(testName)) return null; if (!Discourse.Category.list() && testName === "categories") return null; - if (!validNavNames.contains(testName)) return null; + if (!Discourse.NavItem.NAMES.contains(testName)) return null; - opts = { + return Discourse.NavItem.create({ name: name, hasIcon: name === "unread" || name === "starred", filters: split.splice(1), - category: opts.category - }; - - return Discourse.NavItem.create(opts); + category: opts.category, + }); } }); diff --git a/app/assets/javascripts/discourse/models/top_list.js b/app/assets/javascripts/discourse/models/top_list.js deleted file mode 100644 index bb67154b6c6..00000000000 --- a/app/assets/javascripts/discourse/models/top_list.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - A data model representing a list of top topic lists - - @class TopList - @extends Discourse.Model - @namespace Discourse - @module Discourse -**/ - -Discourse.TopList = Discourse.Model.extend({}); - -Discourse.TopList.reopenClass({ - find: function() { - return PreloadStore.getAndRemove("top_list", function() { - return Discourse.ajax("/top.json"); - }).then(function (result) { - var topList = Discourse.TopList.create({ - can_create_topic: result.can_create_topic, - yearly: Discourse.TopicList.from(result.yearly), - monthly: Discourse.TopicList.from(result.monthly), - weekly: Discourse.TopicList.from(result.weekly), - daily: Discourse.TopicList.from(result.daily) - }); - // disable sorting - topList.setProperties({ - "yearly.sortOrder": undefined, - "monthly.sortOrder": undefined, - "weekly.sortOrder": undefined, - "daily.sortOrder": undefined - }); - return topList; - }); - } -}); diff --git a/app/assets/javascripts/discourse/models/top_list.js.erb b/app/assets/javascripts/discourse/models/top_list.js.erb new file mode 100644 index 00000000000..d595ea67eb0 --- /dev/null +++ b/app/assets/javascripts/discourse/models/top_list.js.erb @@ -0,0 +1,37 @@ +/** + A data model representing a list of top topic lists + + @class TopList + @extends Discourse.Model + @namespace Discourse + @module Discourse +**/ + +Discourse.TopList = Discourse.Model.extend({}); + +Discourse.TopList.reopenClass({ + PERIODS: <%= TopTopic.periods.map(&:to_s) %>, + + find: function(period, category) { + return PreloadStore.getAndRemove("top_lists", function() { + var url = ""; + if (category) { url += category.get("url") + "/l"; } + url += "/top"; + if (period) { url += "/" + period; } + return Discourse.ajax(url + ".json"); + }).then(function (result) { + var topList = Discourse.TopList.create({}); + + _.each(Discourse.TopList.PERIODS, function(period) { + // if there is a list for that period + if (result[period]) { + // instanciate a new topic list with no sorting + topList.set(period, Discourse.TopicList.from(result[period])); + topList.set(period + ".sortOrder", undefined); + } + }); + + return topList; + }); + } +}); diff --git a/app/assets/javascripts/discourse/models/topic_list.js b/app/assets/javascripts/discourse/models/topic_list.js index 9c762f2640d..0eccd3f5ad3 100644 --- a/app/assets/javascripts/discourse/models/topic_list.js +++ b/app/assets/javascripts/discourse/models/topic_list.js @@ -65,8 +65,7 @@ Discourse.TopicList = Discourse.Model.extend({ params.sort_descending = sortOrder.get('descending'); this.set('loaded', false); - var finder = finderFor(this.get('filter'), params); - finder().then(function (result) { + finderFor(this.get('filter'), params).then(function (result) { var newTopics = Discourse.TopicList.topicsFrom(result), topics = self.get('topics'); diff --git a/app/assets/javascripts/discourse/models/user.js b/app/assets/javascripts/discourse/models/user.js index c200722b2ee..1cfb7edb0c2 100644 --- a/app/assets/javascripts/discourse/models/user.js +++ b/app/assets/javascripts/discourse/models/user.js @@ -376,8 +376,6 @@ Discourse.User = Discourse.Model.extend({ }); Discourse.User.reopenClass(Discourse.Singleton, { - - /** Find a `Discourse.User` for a given username. diff --git a/app/assets/javascripts/discourse/routes/application_routes.js b/app/assets/javascripts/discourse/routes/application_routes.js index c4cb09a6d40..7f6c99a763d 100644 --- a/app/assets/javascripts/discourse/routes/application_routes.js +++ b/app/assets/javascripts/discourse/routes/application_routes.js @@ -14,34 +14,48 @@ Discourse.Route.buildRoutes(function() { }); // Generate static page routes - Discourse.StaticController.pages.forEach(function(p) { - router.route(p, { path: "/" + p }); + _.each(Discourse.StaticController.PAGES, function (page) { + router.route(page, { path: '/' + page }); }); - // List routes - this.resource('list', { path: '/' }, function() { + this.resource("list", { path: "/" }, function() { router = this; - // Generate routes for all our filters - Discourse.ListController.filters.forEach(function(filter) { - router.route(filter, { path: "/" + filter }); - router.route(filter, { path: "/" + filter + "/more" }); - router.route(filter + "Category", { path: "/category/:slug/l/" + filter }); - router.route(filter + "Category", { path: "/category/:slug/l/" + filter + "/more" }); - router.route(filter + "Category", { path: "/category/:parentSlug/:slug/l/" + filter }); - router.route(filter + "Category", { path: "/category/:parentSlug/:slug/l/" + filter + "/more" }); + // categories + this.route('categories'); + + // top + this.route('top'); + this.route('topCategory', { path: '/category/:slug/l/top' }); + this.route('topCategoryNone', { path: '/category/:slug/none/l/top' }); + this.route('topCategory', { path: '/category/:parentSlug/:slug/l/top' }); + + // top by periods + _.each(Discourse.TopList.PERIODS, function(period) { + var top = 'top' + period.capitalize(); + router.route(top, { path: '/top/' + period }); + router.route(top, { path: '/top/' + period + '/more' }); + router.route(top + 'Category', { path: '/category/:slug/l/top/' + period }); + router.route(top + 'Category', { path: '/category/:slug/l/top/' + period + '/more' }); + router.route(top + 'CategoryNone', { path: '/category/:slug/none/l/top/' + period }); + router.route(top + 'CategoryNone', { path: '/category/:slug/none/l/top/' + period + '/more' }); + router.route(top + 'Category', { path: '/category/:parentSlug/:slug/l/top/' + period }); + router.route(top + 'Category', { path: '/category/:parentSlug/:slug/l/top/' + period + '/more' }); }); - // homepage - var homepage = Discourse.User.current() ? - Discourse.User.currentProp("homepage") : - Discourse.Utilities.defaultHomepage(); - this.route(homepage, { path: '/' }); + // filters + _.each(Discourse.ListController.FILTERS, function(filter) { + router.route(filter, { path: '/' + filter }); + router.route(filter, { path: '/' + filter + '/more' }); + router.route(filter + 'Category', { path: '/category/:slug/l/' + filter }); + router.route(filter + 'Category', { path: '/category/:slug/l/' + filter + '/more' }); + router.route(filter + 'CategoryNone', { path: '/category/:slug/none/l/' + filter }); + router.route(filter + 'CategoryNone', { path: '/category/:slug/none/l/' + filter + '/more' }); + router.route(filter + 'Category', { path: '/category/:parentSlug/:slug/l/' + filter }); + router.route(filter + 'Category', { path: '/category/:parentSlug/:slug/l/' + filter + '/more' }); + }); - // categories page - this.route('categories', { path: '/categories' }); - - // category + // default filter for a category this.route('category', { path: '/category/:slug' }); this.route('category', { path: '/category/:slug/more' }); this.route('categoryNone', { path: '/category/:slug/none' }); @@ -49,33 +63,31 @@ Discourse.Route.buildRoutes(function() { this.route('category', { path: '/category/:parentSlug/:slug' }); this.route('category', { path: '/category/:parentSlug/:slug/more' }); - // top page - this.route('top', { path: '/top' }); + // homepage + var homepage = Discourse.User.current() ? Discourse.User.currentProp('homepage') : Discourse.Utilities.defaultHomepage(); + this.route(homepage, { path: '/' }); }); // User routes this.resource('user', { path: '/users/:username' }, function() { - this.route('index', { path: '/'} ); - this.resource('userActivity', { path: '/activity' }, function() { - var self = this; - Object.keys(Discourse.UserAction.TYPES).forEach(function (userAction) { - self.route(userAction, { path: userAction.replace("_", "-") }); + router = this; + _.map(Discourse.UserAction.TYPES, function (id, userAction) { + router.route(userAction, { path: userAction.replace('_', '-') }); }); }); this.resource('userPrivateMessages', { path: '/private-messages' }, function() { - this.route('mine', { path: '/mine' }); - this.route('unread', { path: '/unread' }); + this.route('mine'); + this.route('unread'); }); - this.resource('preferences', { path: '/preferences' }, function() { - this.route('username', { path: '/username' }); - this.route('email', { path: '/email' }); + this.resource('preferences', function() { + this.route('username'); + this.route('email'); this.route('about', { path: '/about-me' }); - this.route('avatar', { path: '/avatar' }); }); - this.route('invited', { path: 'invited' }); + this.route('invited'); }); }); diff --git a/app/assets/javascripts/discourse/routes/discourse_route.js b/app/assets/javascripts/discourse/routes/discourse_route.js index 4f3be7f703d..ee556ba9195 100644 --- a/app/assets/javascripts/discourse/routes/discourse_route.js +++ b/app/assets/javascripts/discourse/routes/discourse_route.js @@ -32,7 +32,7 @@ Discourse.Route = Em.Route.extend({ Discourse.set('notifyCount',0); var hideDropDownFunction = $('html').data('hide-dropdown'); - if (hideDropDownFunction) return hideDropDownFunction(); + if (hideDropDownFunction) { hideDropDownFunction(); } } }); diff --git a/app/assets/javascripts/discourse/routes/filtered_list_route.js b/app/assets/javascripts/discourse/routes/filtered_list_route.js index 408f9ea5009..a1d0ec94f3d 100644 --- a/app/assets/javascripts/discourse/routes/filtered_list_route.js +++ b/app/assets/javascripts/discourse/routes/filtered_list_route.js @@ -10,21 +10,8 @@ Discourse.FilteredListRoute = Discourse.Route.extend({ redirect: function() { Discourse.redirectIfLoginRequired(this); }, - deactivate: function() { - this._super(); - - this.controllerFor('list').setProperties({ - canCreateTopic: false, - filterMode: '' - }); - }, - renderTemplate: function() { - this.render('listTopics', { - into: 'list', - outlet: 'listView', - controller: 'listTopics' - }); + this.render('listTopics', { into: 'list', outlet: 'listView', controller: 'listTopics' }); }, setupController: function() { @@ -44,7 +31,16 @@ Discourse.FilteredListRoute = Discourse.Route.extend({ listTopicsController.set('model', topicList); Discourse.FilteredListRoute.scrollToLastPosition(); }); - } + }, + + deactivate: function() { + this._super(); + + this.controllerFor('list').setProperties({ + canCreateTopic: false, + filterMode: '' + }); + }, }); Discourse.FilteredListRoute.reopenClass({ @@ -57,6 +53,10 @@ Discourse.FilteredListRoute.reopenClass({ } }); -Discourse.ListController.filters.forEach(function(filter) { - Discourse["List" + (filter.capitalize()) + "Route"] = Discourse.FilteredListRoute.extend({ filter: filter }); +_.each(Discourse.ListController.FILTERS, function(filter) { + Discourse["List" + filter.capitalize() + "Route"] = Discourse.FilteredListRoute.extend({ filter: filter }); +}); + +_.each(Discourse.TopList.PERIODS, function(period) { + Discourse["ListTop" + period.capitalize() + "Route"] = Discourse.FilteredListRoute.extend({ filter: "top/" + period }); }); diff --git a/app/assets/javascripts/discourse/routes/list_categories_route.js b/app/assets/javascripts/discourse/routes/list_categories_route.js index ed8ec0a7110..8d3c2f3bc20 100644 --- a/app/assets/javascripts/discourse/routes/list_categories_route.js +++ b/app/assets/javascripts/discourse/routes/list_categories_route.js @@ -8,34 +8,18 @@ **/ Discourse.ListCategoriesRoute = Discourse.Route.extend({ - template: 'listCategories', - - redirect: function() { Discourse.redirectIfLoginRequired(this); }, - - actions: { - createCategory: function() { - Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create({ - color: 'AB9364', text_color: 'FFFFFF', hotness: 5, group_permissions: [{group_name: "everyone", permission_type: 1}], - available_groups: Discourse.Site.current().group_names - })); - this.controllerFor('editCategory').set('selectedTab', 'general'); - } - }, - model: function() { - var listTopicsController = this.controllerFor('listTopics'); - if (listTopicsController) { listTopicsController.set('content', null); } + this.controllerFor('listTop').set('content', null); + this.controllerFor('listTopics').set('content', null); return this.controllerFor('list').load('categories'); }, - deactivate: function() { + activate: function() { this._super(); - this.controllerFor('list').set('canCreateCategory', false); + this.controllerFor('list').setProperties({ filterMode: 'categories', category: null }); }, - renderTemplate: function() { - this.render(this.get('template'), { into: 'list', outlet: 'listView' }); - }, + redirect: function() { Discourse.redirectIfLoginRequired(this); }, afterModel: function(categoryList) { this.controllerFor('list').setProperties({ @@ -44,13 +28,23 @@ Discourse.ListCategoriesRoute = Discourse.Route.extend({ }); }, - activate: function() { - this.controllerFor('list').setProperties({ - filterMode: 'categories', - category: null - }); - } + renderTemplate: function() { + this.render('listCategories', { into: 'list', outlet: 'listView' }); + }, + + deactivate: function() { + this._super(); + this.controllerFor('list').set('canCreateCategory', false); + }, + + actions: { + createCategory: function() { + Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create({ + color: 'AB9364', text_color: 'FFFFFF', hotness: 5, group_permissions: [{group_name: 'everyone', permission_type: 1}], + available_groups: Discourse.Site.current().group_names + })); + this.controllerFor('editCategory').set('selectedTab', 'general'); + } + }, }); - - diff --git a/app/assets/javascripts/discourse/routes/list_category_route.js b/app/assets/javascripts/discourse/routes/list_category_route.js index 944535cbdc1..093378bb412 100644 --- a/app/assets/javascripts/discourse/routes/list_category_route.js +++ b/app/assets/javascripts/discourse/routes/list_category_route.js @@ -7,8 +7,17 @@ @module Discourse **/ Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({ + model: function(params) { - return Discourse.Category.findBySlug(Em.get(params, 'slug'), Em.get(params, 'parentSlug')); + this.controllerFor('listTop').set('content', null); + this.controllerFor('listCategories').set('content', null); + return Discourse.Category.findBySlug(params.slug, params.parentSlug); + }, + + activate: function() { + this._super(); + // Add a search context + this.controllerFor('search').set('searchContext', this.modelFor(this.get('routeName')).get('searchContext')); }, setupController: function(controller, category) { @@ -37,33 +46,29 @@ Discourse.ListCategoryRoute = Discourse.FilteredListRoute.extend({ canCreateTopic: topicList.get('can_create_topic'), category: category }); - self.controllerFor('listTopics').set('content', topicList); - self.controllerFor('listTopics').set('category', category); + self.controllerFor('listTopics').setProperties({ + content: topicList, + category: category + }); Discourse.FilteredListRoute.scrollToLastPosition(); }); }, - activate: function() { - this._super(); - - // Add a search context - this.controllerFor('search').set('searchContext', this.modelFor(this.get('routeName')).get('searchContext')); - }, - deactivate: function() { this._super(); - // Clear the search context this.controllerFor('search').set('searchContext', null); } }); -Discourse.ListCategoryNoneRoute = Discourse.ListCategoryRoute.extend({ - noSubcategories: true -}); +Discourse.ListCategoryNoneRoute = Discourse.ListCategoryRoute.extend({ noSubcategories: true }); -Discourse.ListController.filters.forEach(function(filter) { +_.each(Discourse.ListController.FILTERS, function(filter) { Discourse["List" + filter.capitalize() + "CategoryRoute"] = Discourse.ListCategoryRoute.extend({ filter: filter }); + Discourse["List" + filter.capitalize() + "CategoryNoneRoute"] = Discourse.ListCategoryRoute.extend({ filter: filter, noSubcategories: true }); }); - +_.each(Discourse.TopList.PERIODS, function(period) { + Discourse["ListTop" + period.capitalize() + "CategoryRoute"] = Discourse.ListCategoryRoute.extend({ filter: "top/" + period }); + Discourse["ListTop" + period.capitalize() + "CategoryNoneRoute"] = Discourse.ListCategoryRoute.extend({ filter: "top/" + period, noSubcategories: true }); +}); diff --git a/app/assets/javascripts/discourse/routes/list_top_route.js b/app/assets/javascripts/discourse/routes/list_top_route.js index d6a3fb213bd..7b14f4a70e4 100644 --- a/app/assets/javascripts/discourse/routes/list_top_route.js +++ b/app/assets/javascripts/discourse/routes/list_top_route.js @@ -1,26 +1,42 @@ Discourse.ListTopRoute = Discourse.Route.extend({ - model: function() { - return Discourse.TopList.find(); + model: function(params) { + this.controllerFor('listCategories').set('content', null); + this.controllerFor('listTopics').set('content', null); + this.controllerFor('list').set('loading', true); + + var category = Discourse.Category.findBySlug(params.slug, params.parentSlug); + if (category) { this.set('category', category); } + + return Discourse.TopList.find(this.period, category); }, activate: function() { this._super(); - // will mark the "top" navigation item as selected - this.controllerFor('list').setProperties({ - filterMode: 'top', - category: null - }); + this.controllerFor('list').setProperties({ filterMode: 'top', category: null }); + }, + + redirect: function() { Discourse.redirectIfLoginRequired(this); }, + + setupController: function(controller, model) { + var category = this.get('category'), + categorySlug = Discourse.Category.slugFor(category), + url = category === undefined ? 'top' : 'category/' + categorySlug + '/l/top'; + + this.controllerFor('listTop').setProperties({ content: model, category: category }); + this.controllerFor('list').setProperties({ loading: false, filterMode: url }); + + if (category !== undefined) { + this.controllerFor('list').set('category', category); + } + + Discourse.set('title', I18n.t('filters.top.title')); }, renderTemplate: function() { - this.render('top', { into: 'list', outlet: 'listView' }); - }, - - deactivate: function() { - this._super(); - // Clear any filters when we leave the route - Discourse.URL.set('queryParams', null); + this.render('listTop', { into: 'list', outlet: 'listView' }); } }); + +Discourse.ListTopCategoryRoute = Discourse.ListTopRoute.extend({}); diff --git a/app/assets/javascripts/discourse/routes/static_route.js b/app/assets/javascripts/discourse/routes/static_route.js index dca850d229d..2099a2fe3ec 100644 --- a/app/assets/javascripts/discourse/routes/static_route.js +++ b/app/assets/javascripts/discourse/routes/static_route.js @@ -6,16 +6,16 @@ @namespace Discourse @module Discourse **/ -Discourse.StaticController.pages.forEach(function(page) { +_.each(Discourse.StaticController.PAGES, function(page) { - Discourse[(page.capitalize()) + "Route"] = Discourse.Route.extend({ + Discourse[page.capitalize() + "Route"] = Discourse.Route.extend({ renderTemplate: function() { this.render('static'); }, setupController: function() { - var config_key = Discourse.StaticController.configs[page]; + var config_key = Discourse.StaticController.CONFIGS[page]; if (config_key && Discourse.SiteSettings[config_key].length > 0) { Discourse.URL.redirectTo(Discourse.SiteSettings[config_key]); } else { @@ -26,5 +26,3 @@ Discourse.StaticController.pages.forEach(function(page) { }); }); - - diff --git a/app/assets/javascripts/discourse/routes/user_topic_list_routes.js b/app/assets/javascripts/discourse/routes/user_topic_list_routes.js index 5e55e370cbb..e55162b8843 100644 --- a/app/assets/javascripts/discourse/routes/user_topic_list_routes.js +++ b/app/assets/javascripts/discourse/routes/user_topic_list_routes.js @@ -32,7 +32,6 @@ Discourse.UserPrivateMessagesIndexRoute = createPMRoute('index', 'private-messag Discourse.UserPrivateMessagesMineRoute = createPMRoute('mine', 'private-messages-sent'); Discourse.UserPrivateMessagesUnreadRoute = createPMRoute('unread', 'private-messages-unread'); - Discourse.UserActivityTopicsRoute = Discourse.UserTopicListRoute.extend({ userActionType: Discourse.UserAction.TYPES.topics, @@ -45,6 +44,6 @@ Discourse.UserActivityStarredRoute = Discourse.UserTopicListRoute.extend({ userActionType: Discourse.UserAction.TYPES.starred, model: function() { - return Discourse.TopicList.find('starred', {user_id: this.modelFor('user').get('id') }); + return Discourse.TopicList.find('starred', { user_id: this.modelFor('user').get('id') }); } -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars b/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars index a3f35d95c22..cb254e0f05b 100644 --- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars +++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.js.handlebars @@ -37,9 +37,11 @@ {{/if}} + {{categoryLink topic.category}} + {{number topic.posts_count numberKey="posts_long"}} diff --git a/app/assets/javascripts/discourse/templates/list/categories.js.handlebars b/app/assets/javascripts/discourse/templates/list/categories.js.handlebars index 8ab40b0f5be..15c784822d3 100644 --- a/app/assets/javascripts/discourse/templates/list/categories.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/categories.js.handlebars @@ -1,17 +1,14 @@
- - - - - - - + + + + + + {{#each model.categories}} @@ -19,9 +16,7 @@ {{/each}} -
{{i18n categories.category}}{{i18n categories.latest}}{{i18n categories.topics}}{{i18n categories.posts}} - {{#if canEdit}} - - {{/if}} -
{{i18n categories.category}}{{i18n categories.latest}}{{i18n categories.topics}}{{i18n categories.posts}} + {{#if canEdit}}{{/if}} +
- {{#if controller.ordering}} - - {{/if}} + {{#if controller.ordering}}{{/if}} {{categoryLink this allowUncategorized=true}} {{#if unreadTopics}} {{unbound unreadTopics}} @@ -42,7 +37,6 @@ {{{description_excerpt}}}
{{/if}} - {{#if subcategories}}
{{i18n categories.subcategories}} @@ -102,10 +96,7 @@
- - + diff --git a/app/assets/javascripts/discourse/templates/list/top.js.handlebars b/app/assets/javascripts/discourse/templates/list/top.js.handlebars new file mode 100644 index 00000000000..6d6d08a006f --- /dev/null +++ b/app/assets/javascripts/discourse/templates/list/top.js.handlebars @@ -0,0 +1,48 @@ +
+ {{#if redirectedToTopPageReason}} +
+ {{redirectedToTopPageReason}} +
+ {{/if}} + {{#if content.yearly}} +
+

{{i18n filters.top.this_year}}

+ {{basic-topic-list topicList=content.yearly}} + {{#if content.yearly.topics.length}}{{i18n show_more}}{{/if}} +
+ {{/if}} + {{#if content.monthly}} +
+

{{i18n filters.top.this_month}}

+ {{basic-topic-list topicList=content.monthly}} + {{#if content.monthly.topics.length}}{{i18n show_more}}{{/if}} +
+ {{/if}} + {{#if content.weekly}} +
+

{{i18n filters.top.this_week}}

+ {{basic-topic-list topicList=content.weekly}} + {{#if content.weekly.topics.length}}{{i18n show_more}}{{/if}} +
+ {{/if}} + {{#if content.daily}} +
+

{{i18n filters.top.today}}

+ {{basic-topic-list topicList=content.daily}} + {{#if content.daily.topics.length}}{{i18n show_more}}{{/if}} +
+ {{/if}} +
+

+ {{#if hasDisplayedAllTopLists}} + {{#link-to "list.categories"}}{{i18n topic.browse_all_categories}}{{/link-to}} {{i18n or}} {{#link-to 'list.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}}. + {{else}} + {{#link-to "list.categories"}}{{i18n topic.browse_all_categories}}{{/link-to}}, {{#link-to 'list.latest'}}{{i18n topic.view_latest_topics}}{{/link-to}} {{i18n or}} {{i18n filters.top.other_periods}} + {{#unless content.yearly}}{{i18n filters.top.this_year}}{{/unless}} + {{#unless content.monthly}}{{i18n filters.top.this_month}}{{/unless}} + {{#unless content.weekly}}{{i18n filters.top.this_week}}{{/unless}} + {{#unless content.daily}}{{i18n filters.top.today}}{{/unless}} + {{/if}} +

+
+
diff --git a/app/assets/javascripts/discourse/templates/top.js.handlebars b/app/assets/javascripts/discourse/templates/top.js.handlebars deleted file mode 100644 index 3c14b0035db..00000000000 --- a/app/assets/javascripts/discourse/templates/top.js.handlebars +++ /dev/null @@ -1,18 +0,0 @@ -{{#if redirectedToTopPageReason}} -
- {{redirectedToTopPageReason}} -
-{{/if}} -{{#if showThisYear}} -

{{i18n filters.top.this_year}}

- {{basic-topic-list topicList=content.yearly}} -{{/if}} -

{{i18n filters.top.this_month}}

-{{basic-topic-list topicList=content.monthly}} -

{{i18n filters.top.this_week}}

-{{basic-topic-list topicList=content.weekly}} -

{{i18n filters.top.today}}

-{{basic-topic-list topicList=content.daily}} - diff --git a/app/assets/javascripts/discourse/views/list/list_top_view.js b/app/assets/javascripts/discourse/views/list/list_top_view.js new file mode 100644 index 00000000000..6c41f4ccab8 --- /dev/null +++ b/app/assets/javascripts/discourse/views/list/list_top_view.js @@ -0,0 +1,18 @@ +/** + This view handles the rendering of the top lists + + @class ListTopView + @extends Discourse.View + @namespace Discourse + @module Discourse +**/ +Discourse.ListTopView = Discourse.View.extend({ + + didInsertElement: function() { + this._super(); + Em.run.schedule('afterRender', function() { + $('html, body').scrollTop(0); + }); + }, + +}); diff --git a/app/assets/javascripts/discourse/views/nav_item_view.js b/app/assets/javascripts/discourse/views/nav_item_view.js index 8050ac888ac..7403ac7270f 100644 --- a/app/assets/javascripts/discourse/views/nav_item_view.js +++ b/app/assets/javascripts/discourse/views/nav_item_view.js @@ -25,7 +25,7 @@ Discourse.NavItemView = Discourse.View.extend({ name = "category"; } return I18n.t("filters." + name + ".help", extra); - }.property("content.filter"), + }.property("content.name"), name: function() { diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index 3c392ef2771..a83955db090 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -63,6 +63,7 @@ width: 100%; border-collapse: separate; border-spacing: 0; + margin: 0 0 10px; a.title:visited:not(.badge-notification) {color: #888;} > tbody > tr { @@ -230,6 +231,7 @@ } } +#list-area .top-lists h2 { margin: 5px 0 10px; } #topic-list tbody tr.has-excerpt .star { vertical-align: top; diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index f532c550ff5..3456b474dbd 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -1,6 +1,15 @@ class ListController < ApplicationController - before_filter :ensure_logged_in, except: [:latest, :hot, :category, :top, :category_feed, :latest_feed, :hot_feed, :topics_by] + before_filter :ensure_logged_in, except: [ + :topics_by, + # anonymous filters + Discourse.anonymous_filters, Discourse.anonymous_filters.map { |f| "#{f}_feed".to_sym }, + # category + :category, :category_feed, + # top + :top_lists, TopTopic.periods.map { |p| "top_#{p}".to_sym } + ].flatten + before_filter :set_category, only: [:category, :category_feed] skip_before_filter :check_xhr @@ -72,13 +81,15 @@ class ListController < ApplicationController redirect_to latest_path, :status => 301 end - def top + def top_lists + discourse_expires_in 1.minute + top = generate_top_lists respond_to do |format| format.html do @top = top - store_preloaded('top_list', MultiJson.dump(TopListSerializer.new(top, scope: guardian, root: false))) + store_preloaded('top_lists', MultiJson.dump(TopListSerializer.new(top, scope: guardian, root: false))) render 'top' end format.json do @@ -87,6 +98,16 @@ class ListController < ApplicationController end end + TopTopic.periods.each do |period| + define_method("top_#{period}") do + options = build_topic_list_options + user = list_target_user + list = TopicQuery.new(user, options).public_send("list_top_#{period}") + list.more_topics_url = construct_url_with(period, options, "top") + respond(list) + end + end + protected def category_response(extra_opts=nil) @@ -181,19 +202,27 @@ class ListController < ApplicationController def generate_top_lists top = {} - topic_ids = Set.new + options = { + per_page: SiteSetting.topics_per_period_in_summary, + category: params[:category] + } + topic_query = TopicQuery.new(current_user, options) + periods = periods_since(current_user.try(:last_seen_at)) - TopTopic.periods.each do |period| - options = { - per_page: SiteSetting.topics_per_period_in_summary, - except_topic_ids: topic_ids.to_a - } - list = TopicQuery.new(current_user, options).list_top_for(period) - topic_ids.merge(list.topic_ids) - top[period] = list - end + periods.each { |period| top[period] = topic_query.list_top_for(period) } top end + def periods_since(date) + date ||= 1.year.ago + + periods = [:daily] + periods << :weekly if date < 8.days.ago + periods << :monthly if date < 35.days.ago + periods << :yearly if date < 180.days.ago + + periods + end + end diff --git a/app/serializers/top_list_serializer.rb b/app/serializers/top_list_serializer.rb index 753830f33bd..bc6bfd71a1d 100644 --- a/app/serializers/top_list_serializer.rb +++ b/app/serializers/top_list_serializer.rb @@ -1,19 +1,12 @@ class TopListSerializer < ApplicationSerializer - attributes :can_create_topic, - :yearly, - :monthly, - :weekly, - :daily - - def can_create_topic - scope.can_create?(Topic) - end - TopTopic.periods.each do |period| + attribute period + define_method(period) do - TopicListSerializer.new(object[period], scope: scope).as_json + TopicListSerializer.new(object[period], scope: scope).as_json if object[period] end + end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 2c58bb1ac09..e7bab812e67 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -604,6 +604,7 @@ en: latest: "There are no latest topics. That's sad." hot: "There are no hot topics." category: "There are no {{category}} topics." + top: "There are no top topics." bottom: latest: "There are no more latest topics." hot: "There are no more hot topics." @@ -613,6 +614,7 @@ en: unread: "There are no more unread topics." starred: "There are no more starred topics." category: "There are no more {{category}} topics." + top: "There are no more top topics." rank_details: toggle: toggle topic rank details @@ -1122,6 +1124,7 @@ en: this_month: "This month" this_week: "This week" today: "Today" + other_periods: "dig into other periods" redirect_reasons: new_user: "Welcome! As a new visitor, we thought you might like to start with this list of the top discussion topics in the last year." not_seen_in_a_month: "Welcome back! We haven't seen you in a while. These are the top discussion topics since you've been away." diff --git a/config/routes.rb b/config/routes.rb index 7b26573a379..b6e3bea3af8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -106,7 +106,6 @@ Discourse::Application.routes.draw do get "email/unsubscribe/:key" => "email#unsubscribe", as: "email_unsubscribe" post "email/resubscribe/:key" => "email#resubscribe", as: "email_resubscribe" - resources :session, id: USERNAME_ROUTE_FORMAT, only: [:create, :destroy] do collection do post "forgot_password" @@ -195,18 +194,33 @@ Discourse::Application.routes.draw do end resources :user_actions + # We've renamed popular to latest. If people access it we want a permanent redirect. + get "popular" => "list#popular_redirect" + get "popular/more" => "list#popular_redirect" + resources :categories, :except => :show get "category/:id/show" => "categories#show" post "category/:category_id/move" => "categories#move", as: "category_move" - get "category/:category.rss" => "list#category_feed", format: :rss, as: "category_feed" - get "category/:category" => "list#category", as: "category_list" - get "category/:category/none" => "list#category_none", as: "category_list_none" - get "category/:category/more" => "list#category", as: "category_list_more" + get "category/:category" => "list#category" + get "category/:category/more" => "list#category" + get "category/:category/none" => "list#category_none" + get "category/:category/none/more" => "list#category_none" + get "category/:parent_category/:category" => "list#category" + get "category/:parent_category/:category/more" => "list#category" - # We"ve renamed popular to latest. If people access it we want a permanent redirect. - get "popular" => "list#popular_redirect" - get "popular/more" => "list#popular_redirect" + get "top" => "list#top_lists" + get "category/:category/l/top" => "list#top_lists" + get "category/:parent_category/:category/l/top" => "list#top_lists" + + TopTopic.periods.each do |period| + get "top/#{period}" => "list#top_#{period}" + get "top/#{period}/more" => "list#top_#{period}" + get "category/:category/l/top/#{period}" => "list#top_#{period}" + get "category/:category/l/top/#{period}/more" => "list#top_#{period}" + get "category/:parent_category/:category/l/top/#{period}" => "list#top_#{period}" + get "category/:parent_category/:category/l/top/#{period}/more" => "list#top_#{period}" + end Discourse.anonymous_filters.each do |filter| get "#{filter}.rss" => "list##{filter}_feed", format: :rss @@ -215,26 +229,19 @@ Discourse::Application.routes.draw do Discourse.filters.each do |filter| get "#{filter}" => "list##{filter}" get "#{filter}/more" => "list##{filter}" - get "category/:category/l/#{filter}" => "list##{filter}" get "category/:category/l/#{filter}/more" => "list##{filter}" get "category/:parent_category/:category/l/#{filter}" => "list##{filter}" get "category/:parent_category/:category/l/#{filter}/more" => "list##{filter}" end - get "top" => "list#top" - get "category/:category/l/top" => "list#top" - get "category/:parent_category/:category/l/top" => "list#top" - - get "category/:parent_category/:category" => "list#category", as: "category_list_parent" - get "search" => "search#query" # Topics resource get "t/:id" => "topics#show" - delete "t/:id" => "topics#destroy" - put "t/:id" => "topics#update" post "t" => "topics#create" + put "t/:id" => "topics#update" + delete "t/:id" => "topics#destroy" post "topics/timings" get "topics/similar_to" get "topics/created-by/:username" => "list#topics_by", as: "topics_by", constraints: {username: USERNAME_ROUTE_FORMAT} @@ -277,7 +284,6 @@ Discourse::Application.routes.draw do get "raw/:topic_id(/:post_number)" => "posts#markdown" - resources :invites delete "invites" => "invites#destroy" @@ -299,6 +305,6 @@ Discourse::Application.routes.draw do # special case for categories root to: "categories#index", constraints: HomePageConstraint.new("categories"), :as => "categories_index" # special case for top - root to: "list#top", constraints: HomePageConstraint.new("top"), :as => "list_top" + root to: "list#top_lists", constraints: HomePageConstraint.new("top"), :as => "top_lists" end diff --git a/db/migrate/20131223171005_create_top_topics.rb b/db/migrate/20131223171005_create_top_topics.rb index 25bda921d18..974928c96d8 100644 --- a/db/migrate/20131223171005_create_top_topics.rb +++ b/db/migrate/20131223171005_create_top_topics.rb @@ -1,10 +1,13 @@ class CreateTopTopics < ActiveRecord::Migration + PERIODS = [:yearly, :monthly, :weekly, :daily] + SORT_ORDERS = [:posts, :views, :likes] + def change create_table :top_topics, force: true do |t| t.belongs_to :topic - TopTopic.periods.each do |period| - TopTopic.sort_orders.each do |sort| + PERIODS.each do |period| + SORT_ORDERS.each do |sort| t.integer "#{period}_#{sort}_count".to_sym, null: false, default: 0 end end @@ -13,8 +16,8 @@ class CreateTopTopics < ActiveRecord::Migration add_index :top_topics, :topic_id, unique: true - TopTopic.periods.each do |period| - TopTopic.sort_orders.each do |sort| + PERIODS.each do |period| + SORT_ORDERS.each do |sort| add_index :top_topics, "#{period}_#{sort}_count".to_sym, order: 'desc' end end diff --git a/lib/topic_query.rb b/lib/topic_query.rb index 6a3d40e9672..81b736aefeb 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -89,11 +89,17 @@ class TopicQuery score = "#{period}_score" create_list(:top, unordered: true) do |topics| topics.joins(:top_topic) - .where("top_topics.#{score} > 1") + .where("top_topics.#{score} > 0") .order("top_topics.#{score} DESC, topics.bumped_at DESC") end end + TopTopic.periods.each do |period| + define_method("list_top_#{period}") do + list_top_for(period) + end + end + def list_topics_by(user) create_list(:user_topics) do |topics| topics.where(user_id: user.id) diff --git a/test/javascripts/lib/url_test.js b/test/javascripts/lib/url_test.js index 9b8625bc024..ee8afbae2eb 100644 --- a/test/javascripts/lib/url_test.js +++ b/test/javascripts/lib/url_test.js @@ -8,7 +8,7 @@ test("navigatedToHome", function() { mock.expects("refresh").twice(); ok(Discourse.URL.navigatedToHome("/", "/")); - var defaultFilter = "/" + Discourse.ListController.filters[0]; + var defaultFilter = "/" + Discourse.ListController.FILTERS[0]; ok(Discourse.URL.navigatedToHome(defaultFilter, "/")); ok(!Discourse.URL.navigatedToHome("/old", "/new"));