diff --git a/Changelog.md b/Changelog.md index 465547322..55a5dd4e5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ * Disable auto follow back on aspect deletion [#5846](https://github.com/diaspora/diaspora/pull/5846) * Fix only sharing flag for contacts that are receiving [#5848](https://github.com/diaspora/diaspora/pull/5848) * Return 406 when requesting a JSON representation of people/:guid/contacts [#5849](https://github.com/diaspora/diaspora/pull/5849) +* Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852) * Hide manage services link in the publisher on certain pages [#5854](https://github.com/diaspora/diaspora/pull/5854) * Fix notification mails for limited posts [#5877](https://github.com/diaspora/diaspora/pull/5877) * Fix medium and small avatar URLs when using Camo [#5883](https://github.com/diaspora/diaspora/pull/5883) diff --git a/app/models/comment.rb b/app/models/comment.rb index aa86daedb..f6cd6f353 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -47,6 +47,8 @@ class Comment < ActiveRecord::Base after_destroy do self.parent.update_comments_counter + participation = author.participations.where(target_id: post.id).first + participation.unparticipate! if participation.present? end def diaspora_handle diff --git a/app/models/like.rb b/app/models/like.rb index 530fa2aa5..946c1d2ba 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -19,6 +19,8 @@ class Like < Federated::Relayable after_destroy do self.parent.update_likes_counter + participation = author.participations.where(target_id: target.id).first + participation.unparticipate! if participation.present? end xml_attr :positive diff --git a/app/models/participation.rb b/app/models/participation.rb index b36083428..7a1f0d180 100644 --- a/app/models/participation.rb +++ b/app/models/participation.rb @@ -9,6 +9,14 @@ class Participation < Federated::Relayable end end + def unparticipate! + if count == 1 + destroy + else + update!(count: count.pred) + end + end + # NOTE API V1 to be extracted acts_as_api api_accessible :backbone do |t| diff --git a/app/models/user.rb b/app/models/user.rb index de6cc79df..b318465a2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -377,10 +377,10 @@ class User < ActiveRecord::Base retraction = Retraction.for(target) end - if target.is_a?(Post) - opts[:additional_subscribers] = target.resharers - opts[:services] = self.services - end + if target.is_a?(Post) + opts[:additional_subscribers] = target.resharers + opts[:services] = services + end mailman = Postzord::Dispatcher.build(self, retraction, opts) mailman.post diff --git a/app/models/user/social_actions.rb b/app/models/user/social_actions.rb index ccc30b484..2b3ff4a4a 100644 --- a/app/models/user/social_actions.rb +++ b/app/models/user/social_actions.rb @@ -1,6 +1,6 @@ module User::SocialActions def comment!(target, text, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) Comment::Generator.new(self, target, text).create!(opts) end @@ -9,17 +9,17 @@ module User::SocialActions end def like!(target, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) Like::Generator.new(self, target).create!(opts) end def participate_in_poll!(target, answer, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) PollParticipation::Generator.new(self, target, answer).create!(opts) end def reshare!(target, opts={}) - find_or_create_participation!(target) + update_or_create_participation!(target) reshare = build_post(:reshare, :root_guid => target.guid) reshare.save! Postzord::Dispatcher.defer_build_and_post(self, reshare) @@ -48,7 +48,12 @@ module User::SocialActions ) end - def find_or_create_participation!(target) - participations.where(:target_id => target).first || participate!(target) + def update_or_create_participation!(target) + participation = participations.where(target_id: target).first + if participation.present? + participation.update!(count: participation.count.next) + else + participate!(target) + end end end diff --git a/db/migrate/20150404193023_participation_counter.rb b/db/migrate/20150404193023_participation_counter.rb new file mode 100644 index 000000000..a13d075b8 --- /dev/null +++ b/db/migrate/20150404193023_participation_counter.rb @@ -0,0 +1,29 @@ +class ParticipationCounter < ActiveRecord::Migration + def up + add_column :participations, :count, :int, null: false, default: 1 + + posts_count = Post.select("COUNT(posts.id)") + .where("posts.id = participations.target_id") + .where("posts.author_id = participations.author_id") + .to_sql + likes_count = Like.select("COUNT(likes.id)") + .where("likes.target_id = participations.target_id") + .where("likes.author_id = participations.author_id") + .to_sql + comments_count = Comment.select("COUNT(comments.id)") + .where("comments.commentable_id = participations.target_id") + .where("comments.author_id = participations.author_id") + .to_sql + polls_count = PollParticipation.select("COUNT(*)") + .where("poll_participations.author_id = participations.author_id") + .joins(:poll) + .where("polls.status_message_id = participations.target_id") + .to_sql + Participation.update_all("count = (#{posts_count}) + (#{likes_count}) + (#{comments_count}) + (#{polls_count})") + Participation.where(count: 0).delete_all + end + + def down + remove_column :participations, :count + end +end diff --git a/db/schema.rb b/db/schema.rb index 07f4db16c..40ffa940f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150403212139) do +ActiveRecord::Schema.define(version: 20150404193023) do create_table "account_deletions", force: :cascade do |t| t.string "diaspora_handle", limit: 255 @@ -254,12 +254,13 @@ ActiveRecord::Schema.define(version: 20150403212139) do create_table "participations", force: :cascade do |t| t.string "guid", limit: 255 t.integer "target_id", limit: 4 - t.string "target_type", limit: 60, null: false + t.string "target_type", limit: 60, null: false t.integer "author_id", limit: 4 t.text "author_signature", limit: 65535 t.text "parent_author_signature", limit: 65535 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "count", limit: 4, default: 1, null: false end add_index "participations", ["guid"], name: "index_participations_on_guid", length: {"guid"=>191}, using: :btree diff --git a/features/desktop/activity_stream.feature b/features/desktop/activity_stream.feature new file mode 100644 index 000000000..81d0d72f8 --- /dev/null +++ b/features/desktop/activity_stream.feature @@ -0,0 +1,104 @@ +@javascript +Feature: The activity stream + Background: + Given following users exist: + | username | email | + | Bob Jones | bob@bob.bob | + | Alice Smith | alice@alice.alice | + And a user with email "bob@bob.bob" is connected with "alice@alice.alice" + When "alice@alice.alice" has posted a status message with a photo + + Scenario: Sorting + When I sign in as "bob@bob.bob" + + Given I expand the publisher + When I write the status message "A- I like turtles" + And I submit the publisher + + Given I expand the publisher + When I write the status message "B- barack obama is your new bicycle" + And I submit the publisher + + Given I expand the publisher + When I write the status message "C- barack obama is a square" + And I submit the publisher + + When I go to the activity stream page + Then "C- barack obama is a square" should be post 1 + And "B- barack obama is your new bicycle" should be post 2 + And "A- I like turtles" should be post 3 + + When I like the post "A- I like turtles" in the stream + And I comment "Sassy sawfish" on "C- barack obama is a square" + And I like the post "B- barack obama is your new bicycle" in the stream + + When I go to the activity stream page + Then "B- barack obama is your new bicycle" should be post 1 + And "C- barack obama is a square" should be post 2 + And "A- I like turtles" should be post 3 + + Scenario: delete a comment + When I sign in as "bob@bob.bob" + And I am on "alice@alice.alice"'s page + Then I should see "Look at this dog" + When I focus the comment field + And I fill in the following: + | text | is that a poodle? | + And I press "Comment" + + When I go to the activity stream page + Then I should see "Look at this dog" + And I should see "is that a poodle?" + + When I am on "alice@alice.alice"'s page + And I click to delete the first comment + And I confirm the alert + + And I go to the activity stream page + Then I should not see "Look at this dog" + + Scenario: unliking a post + When I sign in as "bob@bob.bob" + And I am on "alice@alice.alice"'s page + Then I should see "Look at this dog" + + When I like the post "Look at this dog" in the stream + And I go to the activity stream page + Then I should see "Look at this dog" + + When I am on "alice@alice.alice"'s page + And I unlike the post "Look at this dog" in the stream + And I go to the activity stream page + Then I should not see "Look at this dog" + + Scenario: multiple participations + When I sign in as "bob@bob.bob" + And I am on "alice@alice.alice"'s page + Then I should see "Look at this dog" + + When I like the post "Look at this dog" in the stream + And I go to the activity stream page + Then I should see "Look at this dog" + + When I am on "alice@alice.alice"'s page + Then I should see "Look at this dog" + + When I focus the comment field + And I fill in the following: + | text | is that a poodle? | + And I press "Comment" + + And I go to the activity stream page + Then I should see "Look at this dog" + + When I am on "alice@alice.alice"'s page + And I unlike the post "Look at this dog" in the stream + And I go to the activity stream page + Then I should see "Look at this dog" + + When I am on "alice@alice.alice"'s page + And I click to delete the first comment + And I confirm the alert + + And I go to the activity stream page + Then I should not see "Look at this dog" diff --git a/features/step_definitions/stream_steps.rb b/features/step_definitions/stream_steps.rb index 901f4da3d..9e38191e5 100644 --- a/features/step_definitions/stream_steps.rb +++ b/features/step_definitions/stream_steps.rb @@ -2,6 +2,10 @@ When /^I (?:like|unlike) the post "([^"]*)" in the stream$/ do |post_text| like_stream_post(post_text) end +Then /^I should see an image in the publisher$/ do + photo_in_publisher.should be_present +end + Then /^"([^"]*)" should be post (\d+)$/ do |post_text, position| stream_element_numbers_content(position).should have_content(post_text) end diff --git a/spec/controllers/status_messages_controller_spec.rb b/spec/controllers/status_messages_controller_spec.rb index 24b8787b8..5fc8ea7ea 100644 --- a/spec/controllers/status_messages_controller_spec.rb +++ b/spec/controllers/status_messages_controller_spec.rb @@ -158,6 +158,13 @@ describe StatusMessagesController, :type => :controller do # response.body.should include('Status message requires a message or at least one photo') # end + it "has one participation" do + post :create, status_message_hash + new_message = StatusMessage.find_by_text(status_message_hash[:status_message][:text]) + expect(new_message.participations.count).to eq(1) + expect(new_message.participations.first.count).to eq(1) + end + context 'with photos' do before do @photo1 = alice.build_post(:photo, :pending => true, :user_file=> File.open(photo_fixture_name), :to => @aspect1.id) diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 2e2a91205..33dc00e6f 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -11,6 +11,23 @@ describe Comment, :type => :model do @status = bob.post(:status_message, :text => "hello", :to => bob.aspects.first.id) end + describe "#destroy" do + before do + @comment = alice.comment!(@status, "why so formal?") + end + + it "should delete a participation" do + expect { @comment.destroy }.to change { Participation.count }.by(-1) + end + + it "should decrease count participation" do + alice.comment!(@status, "Are you there?") + @comment.destroy + participations = Participation.where(target_id: @comment.commentable_id, author_id: @comment.author_id) + expect(participations.first.count).to eq(1) + end + end + describe 'comment#notification_type' do let (:comment) { alice.comment!(@status, "why so formal?") } @@ -60,6 +77,12 @@ describe Comment, :type => :model do alice.comment!(@status, 'hello') }.to change { Comment.count }.by(1) end + + it "should create a participation" do + comment = bob.comment!(@status, "sup dog") + participations = Participation.where(target_id: comment.commentable_id, author_id: comment.author_id) + expect(participations.count).to eq(1) + end end describe 'counter cache' do diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb index 11532bff6..9015d09d3 100644 --- a/spec/models/like_spec.rb +++ b/spec/models/like_spec.rb @@ -14,6 +14,23 @@ describe Like, :type => :model do expect(FactoryGirl.build(:like)).to be_valid end + describe "#destroy" do + before do + @like = alice.like!(@status) + end + + it "should delete a participation" do + expect { @like.destroy }.to change { Participation.count }.by(-1) + end + + it "should decrease count participation" do + alice.comment!(@status, "Are you there?") + @like.destroy + participations = Participation.where(target_id: @like.target_id, author_id: @like.author_id) + expect(participations.first.count).to eq(1) + end + end + describe '#notification_type' do before do @like = alice.like!(@status) diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 658c0cc8e..5c681185d 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -20,4 +20,32 @@ describe Participation, :type => :model do it_should_behave_like 'it is relayable' end + + describe "#unparticipate" do + before do + @status = bob.post(:status_message, text: "hello", to: bob.aspects.first.id) + @like = alice.like!(@status) + end + + it "retract participation" do + @like.author.participations.first.unparticipate! + participations = Participation.where(target_id: @like.target_id, author_id: @like.author_id) + expect(participations.count).to eq(0) + end + + it "retract one of multiple participations" do + comment = alice.comment!(@status, "bro") + comment.author.participations.first.unparticipate! + participations = Participation.where(target_id: @like.target_id, author_id: @like.author_id) + expect(participations.count).to eq(1) + expect(participations.first.count).to eq(1) + end + + it "retract all of multiple participations" do + alice.comment!(@status, "bro") + alice.participations.first.unparticipate! + alice.participations.first.unparticipate! + expect(Participation.where(target_id: @like.target_id, author_id: @like.author_id).count).to eq(0) + end + end end diff --git a/spec/models/user/social_actions_spec.rb b/spec/models/user/social_actions_spec.rb index 74a67608b..ed3041b0f 100644 --- a/spec/models/user/social_actions_spec.rb +++ b/spec/models/user/social_actions_spec.rb @@ -11,9 +11,10 @@ describe User::SocialActions, :type => :model do expect(alice.comment!(@status, "unicorn_mountain").text).to eq("unicorn_mountain") end - it "creates a partcipation" do + it "creates a participation" do expect{ alice.comment!(@status, "bro") }.to change(Participation, :count).by(1) expect(alice.participations.last.target).to eq(@status) + expect(alice.participations.last.count).to eq(1) end it "creates the comment" do @@ -28,7 +29,7 @@ describe User::SocialActions, :type => :model do end describe 'User#like!' do - it "creates a partcipation" do + it "creates a participation" do expect{ alice.like!(@status) }.to change(Participation, :count).by(1) expect(alice.participations.last.target).to eq(@status) end @@ -51,7 +52,7 @@ describe User::SocialActions, :type => :model do @status = bob.post(:status_message, :text => "hello", :to => @bobs_aspect.id) end - it "creates a partcipation" do + it "creates a participation" do expect{ alice.like!(@status) }.to change(Participation, :count).by(1) end @@ -110,4 +111,18 @@ describe User::SocialActions, :type => :model do expect(alice.participate_in_poll!(@status, @answer).poll_answer).to eq(@answer) end end -end \ No newline at end of file + + describe "many actions" do + it "two coments" do + alice.comment!(@status, "bro...") + alice.comment!(@status, "...ther") + expect(alice.participations.last.count).to eq(2) + end + + it "like and comment" do + alice.comment!(@status, "bro...") + alice.like!(@status) + expect(alice.participations.last.count).to eq(2) + end + end +end