diff --git a/app/assets/images/icons-s71323e8d98.png b/app/assets/images/icons-s71323e8d98.png deleted file mode 100644 index 026503b9a..000000000 Binary files a/app/assets/images/icons-s71323e8d98.png and /dev/null differ diff --git a/app/assets/images/icons-sbcb16d92c7.png b/app/assets/images/icons-sbcb16d92c7.png new file mode 100644 index 000000000..8ddc17847 Binary files /dev/null and b/app/assets/images/icons-sbcb16d92c7.png differ diff --git a/app/assets/images/icons/create_participation.png b/app/assets/images/icons/create_participation.png new file mode 100644 index 000000000..7496495b9 Binary files /dev/null and b/app/assets/images/icons/create_participation.png differ diff --git a/app/assets/images/icons/destroy_participation.png b/app/assets/images/icons/destroy_participation.png new file mode 100644 index 000000000..f13d187a6 Binary files /dev/null and b/app/assets/images/icons/destroy_participation.png differ diff --git a/app/assets/javascripts/app/views/stream_post_views.js b/app/assets/javascripts/app/views/stream_post_views.js index 3fe47f3e8..c91ddc27d 100644 --- a/app/assets/javascripts/app/views/stream_post_views.js +++ b/app/assets/javascripts/app/views/stream_post_views.js @@ -23,10 +23,13 @@ app.views.StreamPost = app.views.Post.extend({ "click .remove_post": "destroyModel", "click .hide_post": "hidePost", "click .post_report": "report", - "click .block_user": "blockUser" + "click .block_user": "blockUser", + + "click .create_participation": "createParticipation", + "click .destroy_participation": "destroyParticipation" }, - tooltipSelector : ".timeago, .post_scope, .block_user, .delete", + tooltipSelector : ".timeago, .post_scope, .block_user, .delete, .create_participation, .destroy_participation", initialize : function(){ var personId = this.model.get('author').id; @@ -113,6 +116,22 @@ app.views.StreamPost = app.views.Post.extend({ }); }, + createParticipation: function (evt) { + that = this; + $.post("/posts/" + this.model.get("id") + "/participation", {}, function () { + that.model.set({participation: true}); + that.render(); + }); + }, + + destroyParticipation: function (evt) { + that = this; + $.post("/posts/" + this.model.get("id") + "/participation", { _method: "delete" }, function () { + that.model.set({participation: false}); + that.render(); + }); + }, + focusCommentTextarea: function(evt){ evt.preventDefault(); this.$(".new_comment_form_wrapper").removeClass("hidden"); diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index a3e81c602..9e45891b6 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -279,6 +279,22 @@ ul.as-selections { width: 14px; } } + .create_participation { + display: inline-block; + .icons-create-participation { + height: 14px; + width: 14px; + background: image-url("icons/create_participation.png") center; + } + } + .destroy_participation { + display: inline-block; + .icons-destroy-participation { + height: 14px; + width: 14px; + background: image-url("icons/destroy_participation.png") center; + } + } .delete { display: inline-block; .icons-deletelabel { diff --git a/app/assets/templates/stream-element_tpl.jst.hbs b/app/assets/templates/stream-element_tpl.jst.hbs index 9a6952808..24d976dc8 100644 --- a/app/assets/templates/stream-element_tpl.jst.hbs +++ b/app/assets/templates/stream-element_tpl.jst.hbs @@ -16,6 +16,15 @@
+ {{#if participation}} + +
+
+ {{else}} + +
+
+ {{/if}}
diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb new file mode 100644 index 000000000..a38089b91 --- /dev/null +++ b/app/controllers/participations_controller.rb @@ -0,0 +1,23 @@ +class ParticipationsController < ApplicationController + before_action :authenticate_user! + + def create + post = current_user.find_visible_shareable_by_id(Post, params[:post_id]) + if post + current_user.participate! post + render :nothing => true, :status => :created + else + render :nothing => true, :status => :forbidden + end + end + + def destroy + @participation = current_user.participations.find_by :target_id => params[:post_id] + if @participation + @participation.destroy + render :nothing => true, :status => :no_content + else + render :nothing => true, :status => :unprocessable_entity + end + end +end diff --git a/app/models/comment.rb b/app/models/comment.rb index b71fcaa22..aa86daedb 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -60,7 +60,7 @@ class Comment < ActiveRecord::Base def notification_type(user, person) if self.post.author == user.person return Notifications::CommentOnPost - elsif self.post.comments.where(:author_id => user.person.id) != [] && self.author_id != user.person.id + elsif user.participations.where(:target_id => self.post).exists? && self.author_id != user.person.id return Notifications::AlsoCommented else return false diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb index ff3877bf7..766961e96 100644 --- a/app/presenters/post_presenter.rb +++ b/app/presenters/post_presenter.rb @@ -41,13 +41,14 @@ class PostPresenter :address => @post.address, :poll => @post.poll(), :already_participated_in_poll => already_participated_in_poll, + :participation => participate?, :interactions => { :likes => [user_like].compact, :reshares => [user_reshare].compact, :comments_count => @post.comments_count, :likes_count => @post.likes_count, - :reshares_count => @post.reshares_count, + :reshares_count => @post.reshares_count } } end @@ -86,6 +87,10 @@ class PostPresenter end end + def participate? + user_signed_in? && @current_user.participations.where(:target_id => @post).exists? + end + end class PostInteractionPresenter diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml index 349df2b36..1a28148f5 100644 --- a/config/locales/javascript/javascript.en.yml +++ b/config/locales/javascript/javascript.en.yml @@ -162,6 +162,8 @@ en: hide_nsfw_posts: "Hide #nsfw posts" follow: "Follow" unfollow: "Unfollow" + enable_post_notifications: "Enable notifications for this post" + disable_post_notifications: "Disable notifications for this post" via: "via <%= provider %>" likes: diff --git a/config/routes.rb b/config/routes.rb index 7d6e9785f..3bbdd4f6f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -36,7 +36,7 @@ Diaspora::Application.routes.draw do resources :poll_participations, :only => [:create] resources :likes, :only => [:create, :destroy, :index ] - resources :participations, :only => [:create, :destroy, :index] + resource :participation, :only => [:create, :destroy] resources :comments, :only => [:new, :create, :destroy, :index] end diff --git a/spec/controllers/participations_controller_spec.rb b/spec/controllers/participations_controller_spec.rb new file mode 100644 index 000000000..f9de58b39 --- /dev/null +++ b/spec/controllers/participations_controller_spec.rb @@ -0,0 +1,79 @@ +require 'spec_helper' + +describe ParticipationsController, :type => :controller do + before do + allow(@controller).to receive(:current_user).and_return(alice) + sign_in :user, alice + end + + describe '#create' do + let(:stranger) { FactoryGirl.create(:user) } + + shared_examples 'on a visible post' do + it 'creates the participation' do + post :create, post_id: @post.id + expect(alice.participations.where(:target_id => @post.id)).to exist + expect(response.code).to eq('201') + end + end + + context 'on my own post' do + before do + aspect_to_post = alice.aspects.where(:name => 'generic').first + @post = alice.post :status_message, :text => 'something', :to => aspect_to_post + end + + it_behaves_like 'on a visible post' + end + + context 'on a post from a contact' do + before do + aspect_to_post = bob.aspects.where(:name => 'generic').first + @post = bob.post :status_message, :text => 'something', :to => aspect_to_post + end + + it_behaves_like 'on a visible post' + end + + context 'on a public post from a stranger' do + before do + @post = stranger.post :status_message, :text => 'something', :public => true, :to => 'all' + end + + it_behaves_like 'on a visible post' + end + + context 'on a non visible post' do + before do + @post = stranger.post :status_message, :text => 'something', :public => false, :to => 'all' + end + + it 'should not create the participation' do + post :create, post_id: @post.id + expect(alice.participations.where(:target_id => @post.id)).not_to exist + expect(response.code).to eq('403') + end + end + end + + describe '#destroy' do + let(:post) { FactoryGirl.create(:status_message) } + + context 'on a post you partecipate to' do + before { alice.participate! post } + + it 'should remove participation' do + delete :destroy, post_id: post.id + expect(alice.participations.where(:target_id => post.id)).not_to exist + expect(response.code).to eq('204') + end + end + + context 'on a post you do not partecipate to' do + it 'says it is an unprocessable request' do + delete :destroy, post_id: post.id + expect(response.code).to eq('422') + end + end + end +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index efc7eacdc..2e2a91205 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -12,11 +12,17 @@ describe Comment, :type => :model do end describe 'comment#notification_type' do + let (:comment) { alice.comment!(@status, "why so formal?") } + it "returns 'comment_on_post' if the comment is on a post you own" do - comment = alice.comment!(@status, "why so formal?") expect(comment.notification_type(bob, alice.person)).to eq(Notifications::CommentOnPost) end + it "returns 'also_commented' if the comment is on a post you participate to" do + eve.participate! @status + expect(comment.notification_type(eve, alice.person)).to eq(Notifications::AlsoCommented) + end + it 'returns false if the comment is not on a post you own and no one "also_commented"' do comment = alice.comment!(@status, "I simply felt like issuing a greeting. Do step off.") expect(comment.notification_type(eve, alice.person)).to be false