diff --git a/app/controllers/aspects_controller.rb b/app/controllers/aspects_controller.rb index 2f4317195cc3ec320ee799065e4991b68451738d..ec8af115dc7acf7e21e521f885a7f34e713cefc4 100644 --- a/app/controllers/aspects_controller.rb +++ b/app/controllers/aspects_controller.rb @@ -38,7 +38,7 @@ class AspectsController < ApplicationController :type => ['StatusMessage','ActivityStreams::Photo'], :order => session[:sort_order] + ' DESC', :max_time => params[:max_time].to_i - ).includes(:likes, {:comments => {:author => :profile}}, {:mentions => {:person => :profile}}) + ).includes({:comments => {:author => :profile}}, {:mentions => {:person => :profile}}) @posts = PostsFake.new(posts) if params[:only_posts] diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb index b008dc41a7fc5460d6847f096c8a77aba401ff4f..696a0ba3c2c08731880a1942a11052d2f9d51586 100644 --- a/app/controllers/likes_controller.rb +++ b/app/controllers/likes_controller.rb @@ -9,7 +9,7 @@ class LikesController < ApplicationController respond_to :html, :mobile, :json def create - target = current_user.find_visible_post_by_id params[:post_id] + target = current_user.find_visible_post_by_id params[:status_message_id] positive = (params[:positive] == 'true') ? true : false if target @like = current_user.build_like(:positive => positive, :post => target) @@ -32,7 +32,7 @@ class LikesController < ApplicationController end def destroy - if @like = Like.where(:id => params[:id], :author_id => current_user.person.id).first + if @like = Like.where(:id => params[:id], :author_id => current_user.person.id, :post_id => params[:status_message_id]).first current_user.retract(@like) else respond_to do |format| @@ -41,4 +41,13 @@ class LikesController < ApplicationController end end end + + def index + if target = current_user.find_visible_post_by_id(params[:status_message_id]) + @likes = target.likes.includes(:author => :profile) + render :layout => false + else + render :nothing => true, :status => 404 + end + end end diff --git a/app/helpers/likes_helper.rb b/app/helpers/likes_helper.rb index c9f9b3801675f4229af2bcd70e42b6af7579f69b..0648bed4e465876c11d1793101f662855b5d1094 100644 --- a/app/helpers/likes_helper.rb +++ b/app/helpers/likes_helper.rb @@ -10,9 +10,9 @@ module LikesHelper def like_action(post, current_user=current_user) if current_user.liked?(post) - link_to t('shared.stream_element.unlike'), like_path(current_user.like_for(post)), :method => :delete, :class => 'unlike', :remote => true + link_to t('shared.stream_element.unlike'), status_message_like_path(post, current_user.like_for(post)), :method => :delete, :class => 'unlike', :remote => true else - link_to t('shared.stream_element.like'), likes_path(:positive => 'true', :post_id => post.id ), :method => :post, :class => 'like', :remote => true + link_to t('shared.stream_element.like'), status_message_likes_path(post, :positive => 'true'), :method => :post, :class => 'like', :remote => true end end end diff --git a/app/models/like.rb b/app/models/like.rb index 156775e445f1ebb8a02874dece00cd1b460f73f4..9d549e909d0911b338a2589e5c8728c46449de13 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -15,7 +15,7 @@ class Like < ActiveRecord::Base xml_attr :positive xml_attr :diaspora_handle - belongs_to :post + belongs_to :post, :counter_cache => true belongs_to :author, :class_name => 'Person' validates_uniqueness_of :post_id, :scope => :author_id diff --git a/app/models/user.rb b/app/models/user.rb index 9a2173f8d3d1fa63be46b094be23a5a3e866bfbd..0bab1c58ead984b722c606128cdeafe22a2242e3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -182,10 +182,14 @@ class User < ActiveRecord::Base # Check whether the user has liked a post. Extremely inefficient if the post's likes are not loaded. # @param [Post] post def liked?(post) - if self.like_for(post) - return true + if post.likes.loaded? + if self.like_for(post) + return true + else + return false + end else - return false + Like.exists?(:author_id => self.person.id, :post_id => post.id) end end @@ -193,10 +197,11 @@ class User < ActiveRecord::Base # @param [Post] post # @return [Like] def like_for(post) - post.likes.each do |like| - return like if like.author_id == self.person.id + if post.likes.loaded? + return post.likes.detect{ |like| like.author_id == self.person.id } + else + return Like.where(:author_id => self.person.id, :post_id => post.id).first end - return nil end ######### Mailer ####################### diff --git a/app/views/likes/_likes.haml b/app/views/likes/_likes.haml index 2d0bd5faeaffcf24a3d5b3502a2daee2078f0d87..6536917c00da9bfa083071b815c6fe2329abd8bc 100644 --- a/app/views/likes/_likes.haml +++ b/app/views/likes/_likes.haml @@ -2,11 +2,5 @@ -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -- if likes.size > 0 - .likes_container - .likes - = image_tag('icons/heart.svg') - = link_to t('.people_like_this', :count => likes.length), "#", :class => "expand_likes" - %span.hidden.likes_list - = notification_people_link(nil, likes.map{|x| x.author}) += notification_people_link(nil, likes.map{|x| x.author}) diff --git a/app/views/likes/_likes_container.haml b/app/views/likes/_likes_container.haml new file mode 100644 index 0000000000000000000000000000000000000000..eb39d2136b6b17dd0d8aa070cdc8210507c1f625 --- /dev/null +++ b/app/views/likes/_likes_container.haml @@ -0,0 +1,12 @@ +-# Copyright (c) 2010, Diaspora Inc. This file is +-# licensed under the Affero General Public License version 3 or later. See +-# the COPYRIGHT file. + +- if likes_count > 0 + .likes_container + .likes + = image_tag('icons/heart.svg') + = link_to t('likes.likes.people_like_this', :count => likes_count), status_message_likes_path(post_id), :class => "expand_likes" + %span.hidden.likes_list + /= render 'likes/likes', :likes => likes + diff --git a/app/views/likes/create.js.erb b/app/views/likes/create.js.erb index 67b90b85d064a38f147354b024664116c31d789f..ff6f0ef63c30a3fa94b6a41e94c45f73751eb6e6 100644 --- a/app/views/likes/create.js.erb +++ b/app/views/likes/create.js.erb @@ -1,4 +1,4 @@ $(".like_action", ".stream_element[data-guid=<%=@like.post_id%>]").html("<%= escape_javascript(like_action(@like.post))%>"); -WebSocketReceiver.processLike("<%=@like.post_id%>", "<%= escape_javascript(render("likes/likes", :post_id => @like.post_id, :likes => @like.post.likes)) %>"); +WebSocketReceiver.processLike("<%=@like.post_id%>", "<%= escape_javascript(render("likes/likes_container", :post_id => @like.post_id, :likes_count => @like.post.likes.count)) %>"); diff --git a/app/views/likes/destroy.js.erb b/app/views/likes/destroy.js.erb index 2c9e5e9a4d5115f25e11438bba0470f47acc64ae..0f96633482d80303463fd38161ec318f980b35dd 100644 --- a/app/views/likes/destroy.js.erb +++ b/app/views/likes/destroy.js.erb @@ -1,3 +1,3 @@ $(".like_action", ".stream_element[data-guid=<%=@like.post_id%>]").html("<%= escape_javascript(like_action(@like.post))%>"); -WebSocketReceiver.processLike("<%=@like.post_id%>", "<%= escape_javascript(render("likes/likes", :post_id => @like.post_id, :likes => @like.post.likes)) %>"); +WebSocketReceiver.processLike("<%=@like.post_id%>", "<%= escape_javascript(render("likes/likes_container", :post_id => @like.post_id, :likes_count => @like.post.likes.count)) %>"); diff --git a/app/views/likes/index.html.haml b/app/views/likes/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..3fe13daaaca4e2d8f297350bcac578c01ab280ef --- /dev/null +++ b/app/views/likes/index.html.haml @@ -0,0 +1 @@ += render 'likes', :likes => @likes diff --git a/app/views/photos/show.html.haml b/app/views/photos/show.html.haml index 8fb5000b38eb7fe65b9971dc8c49995e22dc8a29..dfc4d5dba943ae38820c2dfbd4c6ceff738a05c2 100644 --- a/app/views/photos/show.html.haml +++ b/app/views/photos/show.html.haml @@ -65,5 +65,5 @@ #photo_stream.stream.show %div{:data=>{:guid=>parent.id}} .likes_container - = render "likes/likes", :post_id => parent.id, :likes => parent.likes + = render "likes/likes_container", :post_id => parent.id, :likes_count => parent.likes_count = render "comments/comments", :post => parent, :comments => parent.comments, :always_expanded => true diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index 82e70877870c726c8bf813baeee6d10edeb21641..acf88cc8d9adc3a54ee133292e1eb9f4585618b7 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -51,7 +51,7 @@ = link_to t('comments.new_comment.comment'), '#', :class => 'focus_comment_textarea' .likes - - if post.likes.size > 0 - = render "likes/likes", :post_id => post.id, :likes => post.likes, :current_user => current_user + - if post.likes_count > 0 + = render "likes/likes_container", :post_id => post.id, :likes_count => post.likes_count, :current_user => current_user = render "comments/comments", :post => post, :comments => post.comments, :current_user => current_user, :condensed => true, :commenting_disabled => (defined?(@commenting_disabled) && @commenting_disabled) diff --git a/config/routes.rb b/config/routes.rb index d3895c5b964c3777480b918aa19c467f9998fb62..7627953bd2c6e59aceb8cee9e2deb9ea90ccfdd1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,7 +10,10 @@ Diaspora::Application.routes.draw do put 'toggle_contact_visibility' => :toggle_contact_visibility end - resources :status_messages, :only => [:new, :create, :destroy, :show] + resources :status_messages, :only => [:new, :create, :destroy, :show] do + resources :likes, :only => [:create, :destroy, :index] + end + get 'bookmarklet' => 'status_messages#bookmarklet' get 'p/:id' => 'posts#show', :as => 'post' @@ -20,7 +23,6 @@ Diaspora::Application.routes.draw do resources :comments, :only => [:create, :destroy] - resources :likes, :only => [:create, :destroy] resources :conversations do resources :messages, :only => [:create, :show] diff --git a/db/migrate/20110705003445_counter_cache_on_post_likes.rb b/db/migrate/20110705003445_counter_cache_on_post_likes.rb new file mode 100644 index 0000000000000000000000000000000000000000..2d4f55f4cdb8086f1a0fb28894c402c86f84dca4 --- /dev/null +++ b/db/migrate/20110705003445_counter_cache_on_post_likes.rb @@ -0,0 +1,9 @@ +class CounterCacheOnPostLikes < ActiveRecord::Migration + def self.up + add_column :posts, :likes_count, :integer, :default => 0 + end + + def self.down + remove_column :posts, :likes_count + end +end diff --git a/db/schema.rb b/db/schema.rb index 86ba0bfe074413b656e16e8c1131ba72031f2c9d..085facef9ece0468a2ed1ce00e842f70ab749b92 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110623210918) do +ActiveRecord::Schema.define(:version => 20110705003445) do create_table "aspect_memberships", :force => true do |t| t.integer "aspect_id", :null => false @@ -266,6 +266,7 @@ ActiveRecord::Schema.define(:version => 20110623210918) do t.string "actor_url" t.integer "objectId" t.string "status_message_guid" + t.integer "likes_count", :default => 0 end add_index "posts", ["author_id"], :name => "index_posts_on_person_id" diff --git a/public/javascripts/widgets/post.js b/public/javascripts/widgets/post.js index 79bd975c89822c3f3d2fcdccf09d9dfcc6fb312f..14bd300fb84c8d4c958dc89d203fec59ea79c9f5 100644 --- a/public/javascripts/widgets/post.js +++ b/public/javascripts/widgets/post.js @@ -20,11 +20,7 @@ }; this.setUpLikes = function() { - $(this.expandLikesSelector).live("click", function(evt) { - evt.preventDefault(); - $(this).siblings(".likes_list") - .fadeToggle("fast"); - }); + $(this.expandLikesSelector).live("click", this.expandLikes); var likeIt = $(this.likesSelector); @@ -37,7 +33,24 @@ $(this).parent().fadeIn("fast"); }); }; + this.expandLikes = function(evt){ + evt.preventDefault(); + var likesList = $(this).siblings(".likes_list"); + if(likesList.children().length == 0){ + likesList.append("<img alt='loading' src='/images/ajax-loader.gif' />"); + $.ajax({ + url: this.href, + success: function(data){ + likesList.html(data); + likesList.fadeToggle("fast"); + } + }); + }else { + likesList + .fadeToggle("fast"); + } + }; }; Diaspora.widgets.add("post", Post); -})(); \ No newline at end of file +})(); diff --git a/spec/controllers/likes_controller_spec.rb b/spec/controllers/likes_controller_spec.rb index 8ec331a085feb946b6f3aada2ea66c54385dcb30..b5e37fb42d5d62542722d1cf6902ed470b9f6d7e 100644 --- a/spec/controllers/likes_controller_spec.rb +++ b/spec/controllers/likes_controller_spec.rb @@ -12,18 +12,17 @@ describe LikesController do @aspect1 = @user1.aspects.first @aspect2 = @user2.aspects.first - @controller.stub(:current_user).and_return(alice) sign_in :user, @user1 end describe '#create' do let(:like_hash) { {:positive => 1, - :post_id => "#{@post.id}"} + :status_message_id => "#{@post.id}"} } let(:dislike_hash) { {:positive => 0, - :post_id => "#{@post.id}"} + :status_message_id => "#{@post.id}"} } context "on my own post" do @@ -72,6 +71,29 @@ describe LikesController do end end + describe '#index' do + before do + @message = alice.post(:status_message, :text => "hey", :to => @aspect1.id) + end + it 'returns a 404 for a post not visible to the user' do + sign_in eve + get :index, :status_message_id => @message.id + end + + it 'returns an array of likes for a post' do + like = bob.build_like(:positive => true, :post => @message) + like.save! + + get :index, :status_message_id => @message.id + assigns[:likes].map(&:id).should == @message.likes.map(&:id) + end + + it 'returns an empty array for a post with no likes' do + get :index, :status_message_id => @message.id + assigns[:likes].should == [] + end + end + describe '#destroy' do before do @message = bob.post(:status_message, :text => "hey", :to => @aspect1.id) @@ -81,7 +103,7 @@ describe LikesController do it 'lets a user destroy their like' do expect { - delete :destroy, :format => "js", :post_id => @like.post_id, :id => @like.id + delete :destroy, :format => "js", :status_message_id => @like.post_id, :id => @like.id }.should change(Like, :count).by(-1) response.status.should == 200 end @@ -91,7 +113,7 @@ describe LikesController do like2.save expect { - delete :destroy, :format => "js", :post_id => like2.post_id, :id => like2.id + delete :destroy, :format => "js", :status_message_id => like2.post_id, :id => like2.id }.should_not change(Like, :count) response.status.should == 403 diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb index 19c0b496319b8a403a3081b728c7d7580029308e..ade64c299960ac7489585a9d25257e6de27114b2 100644 --- a/spec/models/like_spec.rb +++ b/spec/models/like_spec.rb @@ -56,6 +56,14 @@ describe Like do end end + describe 'counter cache' do + it 'increments the counter cache on its post' do + lambda { + @alice.like(1, :post => @status) + }.should change{ @status.reload.likes_count }.by(1) + end + end + describe 'xml' do before do @liker = Factory.create(:user)