views:

89

answers:

5

My application has news, posts, comments and various other common things. I'd like to be able to keep track of what a user has viewed so that they can be greeted with "4 new Posts" and that type of thing.

At the moment I'm just reporting the number of 'New Posts' with something like:

Post.find(:all, :conditions => [ "updated_at > ?", session[:user_last_login]]).length

Obviously that's only partly as good.

Before I go making a mess of my whole application, is there what's the simplest way I can achieve what I've described, and does rails have any bits to help me out?

Also, is there a common name for this kind of functionality which might aid my googling?

Thanks.

A: 

You need to keep track of which posts have been viewed by the user. If the user has to view the posts in order, you can simply keep track of the last one seen. If they can be viewed out of order then you need to keep a relation indicating whether the user has seen a particular post. To get the "new" posts, you select posts which don't have a matching relation in that table for the current user. Obviously, whenever a user views a post you need to update the column or relation that you are using to track the user's viewing history.

tvanfosson
A: 

Precisely what tvanfosson said. Because, your example will fail when the user logs in but does not view the unread messages.

Set the default value of a post to new and set it to viewed on your first render. In Ruby on Rails, the first render would typically be the show action.

Swanand
+1  A: 

A fairly typical approach to this is to keep track of when a user last viewed a particularly object (or collection of objects). Then, provided you have a created_at or updated_at on the objects you want to keep track of viewings of you can count the number of, say, posts that have changed since the last time the user viewed the news item.

So, you might have a class like:

class NewsItemTrack < ActiveRecord::Base

  belongs_to :news_item
  belongs_to :user

  def self.track(news_item_id, user_id)
    nt = self.find_by_news_item_id_and_user_id(news_item_id, user_id)
    if nt.nil?
      nt = self.create(:news_item_id => news_item_id, :user_id => user_id, :last_viewed_at => Time.now)
    else
      nt.update_attribute(:last_viewed_at, Time.now)    
    end
  end

  def new_comment_count
    Comment.count(:all, :conditions => ['news_item_id = ? and created_at > ?', self.news_item_id, self.last_viewed_at]
  end
end

Where NewsItemTrack.track will record the fact that the user has viewed the news item in question. Then, when you want to get how many new comments there are on a news item you'd do:

NewsItemTrack.find_by_news_item_id_and_user_id(123, 456).new_comment_count
Shadwell
+1  A: 

I would use a join table with a polymorphic relationship - using the plugin (gem?) has_many_polymorphs.

You could have a join table called 'viewings' and associated model Viewing. h_m_p will give you methods like 'viewed_posts', 'viewed_comments', 'viewed_news' etc (News is a bad model name, it should be a singular word like NewsItem with table called news_items, or something similar).

google has_many_polymorphs for the main page, there's examples and stuff.

Max Williams
+1  A: 

You could have a look at ActsAsReadable by Michael Bleigh

JimNeath