diff --git a/app/assets/javascripts/discourse/models/category.js b/app/assets/javascripts/discourse/models/category.js index fd6ad4e26da..965f68434a2 100644 --- a/app/assets/javascripts/discourse/models/category.js +++ b/app/assets/javascripts/discourse/models/category.js @@ -34,6 +34,7 @@ Discourse.Category = Discourse.Model.extend({ return Discourse.getURL("/category/") + (this.get('slug')); }.property('name'), + style: function() { return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_color')) + ";"; }.property('color', 'text_color'), @@ -101,7 +102,15 @@ Discourse.Category = Discourse.Model.extend({ latestTopic: function(){ return this.get("topics")[0]; - }.property("topics") + }.property("topics"), + + unreadTopics: function(){ + return Discourse.TopicTrackingState.current().countUnread(this.get('name')); + }.property('Discourse.TopicTrackingState.current.messageCount'), + + newTopics: function(){ + return Discourse.TopicTrackingState.current().countNew(this.get('name')); + }.property('Discourse.TopicTrackingState.current.messageCount') }); diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js index af80f678cd5..cfe6afc1bef 100644 --- a/app/assets/javascripts/discourse/models/topic.js +++ b/app/assets/javascripts/discourse/models/topic.js @@ -87,6 +87,10 @@ Discourse.Topic = Discourse.Model.extend({ return this.urlForPostNumber(this.get('highest_post_number')); }.property('url', 'highest_post_number'), + lastPosterUrl: function() { + return Discourse.getURL("/users/") + this.get("last_poster.username"); + }.property('last_poster'), + // The amount of new posts to display. It might be different than what the server // tells us if we are still asynchronously flushing our "recently read" data. // So take what the browser has seen into consideration. diff --git a/app/assets/javascripts/discourse/models/topic_tracking_state.js b/app/assets/javascripts/discourse/models/topic_tracking_state.js index 80c85b95313..882d0dd6af3 100644 --- a/app/assets/javascripts/discourse/models/topic_tracking_state.js +++ b/app/assets/javascripts/discourse/models/topic_tracking_state.js @@ -129,20 +129,23 @@ Discourse.TopicTrackingState = Discourse.Model.extend({ this.set("messageCount", this.get("messageCount") + 1); }, - countNew: function(){ + countNew: function(category_name){ return _.chain(this.states) .where({last_read_post_number: null}) + .where(function(topic){ return topic.category_name === category_name || !category_name;}) .value() .length; }, - countUnread: function(){ - var count = 0; - _.each(this.states, function(topic){ - count += (topic.last_read_post_number !== null && - topic.last_read_post_number < topic.highest_post_number) ? 1 : 0; - }); - return count; + countUnread: function(category_name){ + return _.chain(this.states) + .where(function(topic){ + return topic.last_read_post_number !== null && + topic.last_read_post_number < topic.highest_post_number; + }) + .where(function(topic){ return topic.category_name === category_name || !category_name;}) + .value() + .length; }, countCategory: function(category) { diff --git a/app/assets/javascripts/discourse/templates/list/basic_topic_list.js.handlebars b/app/assets/javascripts/discourse/templates/list/basic_topic_list.js.handlebars index 2e488e3ef0d..2cf0896d154 100644 --- a/app/assets/javascripts/discourse/templates/list/basic_topic_list.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/basic_topic_list.js.handlebars @@ -46,7 +46,7 @@ {{unboundAge created_at}} - {{unboundAge bumped_at}} + {{unboundAge bumped_at}} {{else}} @@ -62,4 +62,4 @@
{{i18n choose_topic.none_found}}
-{{/if}} \ No newline at end of file +{{/if}} diff --git a/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars b/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars index c2e4e5bf760..6bb88dfb71a 100644 --- a/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/wide_categories.js.handlebars @@ -14,6 +14,12 @@ {{#each model.categories}} {{categoryLink this}} + {{#if unreadTopics}} + {{unbound unreadTopics}} + {{/if}} + {{#if newTopics}} + {{unbound newTopics}} + {{/if}} {{#if description_excerpt}}
{{{description_excerpt}}} @@ -25,12 +31,15 @@ {{/each}} {{number topic_count}} - {{number posts_total}} + {{number post_count}} {{#with latestTopic}} {{topicStatus topic=this}} {{{topicLink this}}} - +
+ {{i18n categories.by}} {{unbound last_poster.username}} + {{unboundAge last_posted_at}} +
{{/with}} diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index c7487f13d11..1fe45e8b627 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -9,7 +9,14 @@ class CategoriesController < ApplicationController def index @description = SiteSetting.site_description - @list = CategoryList.new(guardian) + wide_mode = SiteSetting.enable_wide_category_list + + options = {} + options[:latest_post_only] = params[:latest_post_only] || wide_mode + + @list = CategoryList.new(guardian,options) + + @list.draft_key = Draft::NEW_TOPIC @list.draft_sequence = DraftSequence.current(current_user, Draft::NEW_TOPIC) diff --git a/app/models/category.rb b/app/models/category.rb index df86fe8d036..c2ba4456988 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -13,6 +13,7 @@ class Category < ActiveRecord::Base end belongs_to :user + belongs_to :latest_post, class_name: "Post" has_many :topics has_many :category_featured_topics @@ -227,6 +228,26 @@ SQL end end + def update_latest + latest_post_id = Post + .order("posts.created_at desc") + .where("NOT hidden") + .joins("join topics on topics.id = topic_id") + .where("topics.category_id = :id", id: self.id) + .limit(1) + .pluck("posts.id") + .first + + latest_topic_id = Topic + .order("topics.created_at desc") + .where("visible") + .where("topics.category_id = :id", id: self.id) + .limit(1) + .pluck("topics.id") + .first + + self.update_attributes(latest_topic_id: latest_topic_id, latest_post_id: latest_post_id) + end def self.resolve_permissions(permissions) read_restricted = true diff --git a/app/models/category_list.rb b/app/models/category_list.rb index 94dd860a093..a3df31e9f87 100644 --- a/app/models/category_list.rb +++ b/app/models/category_list.rb @@ -8,10 +8,11 @@ class CategoryList :draft_key, :draft_sequence - def initialize(guardian=nil) + def initialize(guardian=nil, options = {}) @guardian = guardian || Guardian.new + @options = options - find_relevant_topics + find_relevant_topics unless latest_post_only? find_categories prune_empty @@ -21,6 +22,10 @@ class CategoryList private + def latest_post_only? + @options[:latest_post_only] + end + # Retrieve a list of all the topics we'll need def find_relevant_topics @topics_by_category_id = {} @@ -47,16 +52,35 @@ class CategoryList .order('COALESCE(categories.topics_month, 0) DESC') .order('COALESCE(categories.topics_year, 0) DESC') + if latest_post_only? + @categories = @categories.includes(:latest_post => :topic ) + end + @categories = @categories.to_a - @categories.each do |c| - topics_in_cat = @topics_by_category_id[c.id] - if topics_in_cat.present? - c.displayable_topics = [] - topics_in_cat.each do |topic_id| - topic = @topics_by_id[topic_id] - if topic.present? - topic.category = c - c.displayable_topics << topic + + if latest_post_only? + @all_topics = [] + @categories.each do |c| + if c.latest_post && c.latest_post.topic + c.displayable_topics = [c.latest_post.topic] + topic = c.latest_post.topic + topic.include_last_poster = true # hint for serialization + @all_topics << topic + end + end + end + + if @topics_by_category_id + @categories.each do |c| + topics_in_cat = @topics_by_category_id[c.id] + if topics_in_cat.present? + c.displayable_topics = [] + topics_in_cat.each do |topic_id| + topic = @topics_by_id[topic_id] + if topic.present? + topic.category = c + c.displayable_topics << topic + end end end end diff --git a/app/models/post.rb b/app/models/post.rb index d12279e0c59..d1bfd3e6fc2 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -84,6 +84,9 @@ class Post < ActiveRecord::Base super update_flagged_posts_count TopicLink.extract_from(self) + if topic && topic.category_id + topic.category.update_latest + end end # The key we use in redis to ensure unique posts diff --git a/app/models/topic.rb b/app/models/topic.rb index 6a7e3504872..0df39d989b5 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -98,6 +98,7 @@ class Topic < ActiveRecord::Base attr_accessor :user_data attr_accessor :posters # TODO: can replace with posters_summary once we remove old list code attr_accessor :topic_list + attr_accessor :include_last_poster # The regular order scope :topic_list_order, lambda { order('topics.bumped_at desc') } diff --git a/app/serializers/category_detailed_serializer.rb b/app/serializers/category_detailed_serializer.rb index 2d8841f4723..0971b5840ad 100644 --- a/app/serializers/category_detailed_serializer.rb +++ b/app/serializers/category_detailed_serializer.rb @@ -6,6 +6,7 @@ class CategoryDetailedSerializer < ApplicationSerializer :text_color, :slug, :topic_count, + :post_count, :topics_week, :topics_month, :topics_year, diff --git a/app/serializers/listable_topic_serializer.rb b/app/serializers/listable_topic_serializer.rb index b0b0bbfe6e9..9a304e0163b 100644 --- a/app/serializers/listable_topic_serializer.rb +++ b/app/serializers/listable_topic_serializer.rb @@ -19,6 +19,12 @@ class ListableTopicSerializer < BasicTopicSerializer :closed, :archived + has_one :last_poster, serializer: BasicUserSerializer, embed: :objects + + def include_associations! + include! :last_poster if object.include_last_poster + end + def bumped object.created_at < object.bumped_at end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 146eb7d626e..99bde17a7c7 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -189,6 +189,7 @@ en: posts: "Posts" topics: "Topics" latest: "Latest" + by: "by" user: said: "{{username}} said:" @@ -591,6 +592,12 @@ en: private_message: 'Start a private message' list: 'Topics' new: 'new topic' + new_topics: + one: '1 new topic' + other: '{{count}} new topics' + unread_topics: + one: '1 unread topic' + other: '{{count}} unread topics' title: 'Topic' loading_more: "Loading more Topics..." loading: 'Loading topic...' diff --git a/db/migrate/20131017030605_add_latest_to_categories.rb b/db/migrate/20131017030605_add_latest_to_categories.rb new file mode 100644 index 00000000000..305aa6c4e10 --- /dev/null +++ b/db/migrate/20131017030605_add_latest_to_categories.rb @@ -0,0 +1,33 @@ +class AddLatestToCategories < ActiveRecord::Migration + def up + add_column :categories, :latest_post_id, :integer + add_column :categories, :latest_topic_id, :integer + + execute <