diff --git a/app/controllers/aspects_controller.rb b/app/controllers/aspects_controller.rb index 2f4317195..ec8af115d 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 b008dc41a..696a0ba3c 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/aspect_global_helper.rb b/app/helpers/aspect_global_helper.rb index dac264005..4398183f5 100644 --- a/app/helpers/aspect_global_helper.rb +++ b/app/helpers/aspect_global_helper.rb @@ -9,35 +9,6 @@ module AspectGlobalHelper end end - def aspect_badges(aspects, opts={}) - str = '' - aspects.each do |aspect| - str << aspect_badge(aspect, opts) - end - str.html_safe - end - - def aspect_badge(aspect, opts={}) - str = "" - link = opts.delete(:link) - if !link - str << link_to(aspect.name, "#", 'data-guid' => aspect.id, :class => 'hard_aspect_link').html_safe - else - str << link_for_aspect(aspect).html_safe - end - str << "" - end - - def aspect_links(aspects, opts={}) - str = "" - aspects.each do |aspect| - str << '
  • ' - str << link_for_aspect(aspect, :params => opts, 'data-guid' => aspect.id, :class => 'hard_aspect_link').html_safe - str << '
  • ' - end - str.html_safe - end - def link_for_aspect(aspect, opts={}) opts[:params] ||= {} params ||= {} diff --git a/app/helpers/likes_helper.rb b/app/helpers/likes_helper.rb index c9f9b3801..0648bed4e 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 156775e44..9d549e909 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 9a2173f8d..0bab1c58e 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/aspects/index.html.haml b/app/views/aspects/index.html.haml index 0df73cd20..26824860d 100644 --- a/app/views/aspects/index.html.haml +++ b/app/views/aspects/index.html.haml @@ -6,7 +6,7 @@ - content_for :head do = include_javascripts :home -.span-5.leftNavBar +.span-5.leftNavBar.fixed #home_user_badge = owner_image_link %h3 @@ -15,7 +15,7 @@ .section = render 'aspects/aspect_listings' -.span-13.append-1 +.span-13.append-1.prepend-5 #aspect_stream_container.stream_container = render 'aspect_stream', :aspect => @aspect, diff --git a/app/views/likes/_likes.haml b/app/views/likes/_likes.haml index 2d0bd5fae..6536917c0 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 000000000..eb39d2136 --- /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 67b90b85d..ff6f0ef63 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 2c9e5e9a4..0f9663348 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 000000000..3fe13daaa --- /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 8fb5000b3..dfc4d5dba 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 82e708778..4933e2c38 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -32,26 +32,31 @@ .info - if post.public? - %span.aspect_badges - %span.aspect_badge.public - = t('the_world') - - elsif post.author.owner_id == current_user.id - %span.aspect_badges - = aspect_badges(aspects_with_post(all_aspects, post), :link => true) + %span.post_scope{:title => t('.viewable_to_anyone')} + = t('public') + · + - else + - if post.author.owner_id == current_user.id + %span.post_scope{:title => t('.shared_with', :aspect_names => aspects_with_post(all_aspects, post).map!{|a| a.name}.join(', '))} + = t('limited') + - else + %span.post_scope + = t('limited') + · %span.via - if post.activity_streams? = t('.via', :link => link_to("#{post.provider_display_name}", post.actor_url)).html_safe + · - unless (defined?(@commenting_disabled) && @commenting_disabled) - · %span.like_action = like_action(post, current_user) · = 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/app/views/tokens/show.html.haml b/app/views/tokens/show.html.haml index 7add09dca..4b9ef98c5 100644 --- a/app/views/tokens/show.html.haml +++ b/app/views/tokens/show.html.haml @@ -14,7 +14,7 @@ %hr .cubbies_infographic.span-24{:style => "text-align:left;position:relative;"} - = image_tag '/images/cubbies_screenshot2.png', :height => 452, :class => "cubbies_screenshot" + = image_tag '/images/cubbies_screenshot2.png', :height => 452, :width => 600, :class => "cubbies_screenshot" = image_tag '/images/cubbies_collage.png', :class => "cubbies_collage" .span-24.last{:style => "text-align:right;"} diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 57586a2df..d3ec11df4 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -26,7 +26,8 @@ en: are_you_sure: "Are you sure?" fill_me_out: "Fill me out" back: "Back" - the_world: "the world" + public: "Public" + limited: "Limited" search: "Search" find_people: "Find people" _home: "Home" @@ -657,10 +658,12 @@ en: contact_list: all_contacts: "All contacts" stream_element: + viewable_to_anyone: "This post is viewable to anyone on the web" via: "via %{link}" like: "Like" unlike: "Unlike" dislike: "Dislike" + shared_with: "Shared with: %{aspect_names}" footer: logged_in_as: "logged in as %{name}" your_aspects: "your aspects" diff --git a/config/routes.rb b/config/routes.rb index d3895c5b9..7627953bd 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 000000000..2d4f55f4c --- /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 86ba0bfe0..085facef9 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/lib/tasks/cruise.rake b/lib/tasks/cruise.rake index 1e2a31cbf..ccc3e1b5f 100644 --- a/lib/tasks/cruise.rake +++ b/lib/tasks/cruise.rake @@ -16,7 +16,7 @@ namespace :cruise do end task :migrate do - system('bundle exec rake db:migrate') + system('bundle exec rake db:schema:load') exit_status = $?.exitstatus raise "db:migrate failed!" unless exit_status == 0 end diff --git a/public/images/cubbies_screenshot.png b/public/images/cubbies_screenshot.png deleted file mode 100644 index 7dad8817e..000000000 Binary files a/public/images/cubbies_screenshot.png and /dev/null differ diff --git a/public/images/cubbies_screenshot2.png b/public/images/cubbies_screenshot2.png index d8347fc6c..573e88b23 100644 Binary files a/public/images/cubbies_screenshot2.png and b/public/images/cubbies_screenshot2.png differ diff --git a/public/images/cubbies_settings.png b/public/images/cubbies_settings.png deleted file mode 100644 index ca9bef480..000000000 Binary files a/public/images/cubbies_settings.png and /dev/null differ diff --git a/public/javascripts/widgets/post.js b/public/javascripts/widgets/post.js index 79bd975c8..14bd300fb 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("loading"); + $.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/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 0182bf439..853ac3ea9 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -386,7 +386,7 @@ ul.dropdown p :margin - :top 4px + :top 0 :bottom 10px :font :size 12px @@ -476,10 +476,14 @@ ul.dropdown :margin :bottom 5px - .post_initial_info .details :color #aaa + :margin + :bottom 5px + +.post_scope + :cursor default .time, .timeago, @@ -2085,22 +2089,11 @@ h3,h4 .stream .avatar :float left - .arrow, - - .stream_element - .aspect_badges - @include opacity(0.5) - - &:hover - .aspect_badges - @include opacity(1) .aspect_badge :position relative :margin :bottom 0 - .arrow - :top -0.1em .aspect_badge :top -0.2em @@ -3058,3 +3051,6 @@ ul.left_nav :height 450px :margin :top 30px + +.fixed + :position fixed diff --git a/spec/controllers/likes_controller_spec.rb b/spec/controllers/likes_controller_spec.rb index 8ec331a08..b5e37fb42 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 19c0b4963..ade64c299 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)