Introduce like-interactions.js

Adapt to latest development

User likes
 Set css class for inline likes on comment

Re-set participation on comment likes

Co-authored-by: Thorsten Claus <ThorstenClaus@web.de>
This commit is contained in:
flaburgan
2019-01-14 00:31:38 +01:00
committed by Benjamin Neff
parent 82ff57a750
commit 8d6548b610
20 changed files with 348 additions and 169 deletions

View File

@@ -12,12 +12,17 @@ app.collections.Comments = Backbone.Collection.extend({
make : function(text) {
var self = this;
var comment = new app.models.Comment({ "text": text });
var comment = new app.models.Comment({"text": text}, {post: this.post});
var deferred = comment.save({}, {
url: "/posts/"+ this.post.id +"/comments",
success: function() {
comment.set({author: app.currentUser.toJSON(), parent: self.post });
// Need interactions after make
comment.interactions = new app.models.Post.LikeInteractions(
_.extend({comment: comment, post: self.post}, comment.get("interactions"))
);
self.add(comment);
}
});

View File

@@ -4,10 +4,11 @@ app.collections.Likes = Backbone.Collection.extend({
model: app.models.Like,
initialize : function(models, options) {
this.url = (options.post != null) ?
// A comment- like has a post reference and a comment reference
this.url = (options.comment != null) ?
// not delegating to post.url() because when it is in a stream collection it delegates to that url
"/posts/" + options.post.id + "/likes" :
"/comments/" + options.comment.id + "/likes";
"/comments/" + options.comment.id + "/likes" :
"/posts/" + options.post.id + "/likes";
}
});
// @license-end

View File

@@ -3,51 +3,15 @@
app.models.Comment = Backbone.Model.extend({
urlRoot: "/comments",
initialize: function() {
this.likes = new app.collections.Likes(this.get("likes"), {comment: this});
},
// Copied from Post.Interaction. To be merged in an "interactable" class once comments can be commented too
likesCount: function() {
return this.get("likes_count");
},
userLike: function() {
return this.likes.select(function(like) {
return like.get("author") && like.get("author").guid === app.currentUser.get("guid");
})[0];
},
toggleLike: function() {
if (this.userLike()) {
this.unlike();
} else {
this.like();
}
},
like: function() {
var self = this;
this.likes.create({}, {
success: function() {
self.post.set({participation: true});
self.trigger("change");
self.set({"likes_count": self.get("likes_count") + 1});
self.likes.trigger("change");
},
error: function(model, response) {
app.flashMessages.handleAjaxError(response);
}
});
},
unlike: function() {
var self = this;
this.userLike().destroy({success: function() {
self.trigger("change");
self.set({"likes_count": self.get("likes_count") - 1});
self.likes.trigger("change");
}});
initialize: function(model, options) {
options = options || {};
this.post = model.post || options.post || this.collection.post;
this.interactions = new app.models.Post.LikeInteractions(
_.extend({comment: this, post: this.post}, this.get("interactions"))
);
this.likes = this.interactions.likes;
this.likesCount = this.attributes.likes_count;
this.userLike = this.interactions.userLike();
}
});
// @license-end

View File

@@ -1,75 +1,29 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//require ../post
//= require ./like_interactions
app.models.Post.Interactions = Backbone.Model.extend({
initialize : function(options){
app.models.Post.Interactions = app.models.Post.LikeInteractions.extend({
initialize: function(options) {
app.models.Post.LikeInteractions.prototype.initialize.apply(this, arguments);
this.post = options.post;
this.comments = new app.collections.Comments(this.get("comments"), {post : this.post});
this.likes = new app.collections.Likes(this.get("likes"), {post : this.post});
this.reshares = new app.collections.Reshares(this.get("reshares"), {post : this.post});
this.comments = new app.collections.Comments(this.get("comments"), {post: this.post});
this.reshares = new app.collections.Reshares(this.get("reshares"), {post: this.post});
},
likesCount : function(){
return this.get("likes_count");
},
resharesCount : function(){
resharesCount: function() {
return this.get("reshares_count");
},
commentsCount : function(){
commentsCount: function() {
return this.get("comments_count");
},
userLike : function(){
return this.likes.select(function(like){
return like.get("author") && like.get("author").guid === app.currentUser.get("guid");
})[0];
},
userReshare : function(){
userReshare: function() {
return this.reshares.select(function(reshare){
return reshare.get("author") && reshare.get("author").guid === app.currentUser.get("guid");
})[0];
},
toggleLike : function() {
if(this.userLike()) {
this.unlike();
} else {
this.like();
}
},
like : function() {
var self = this;
this.likes.create({}, {
success: function() {
self.post.set({participation: true});
self.trigger("change");
self.set({"likes_count" : self.get("likes_count") + 1});
self.likes.trigger("change");
},
error: function(model, response) {
app.flashMessages.handleAjaxError(response);
}
});
},
unlike : function() {
var self = this;
this.userLike().destroy({success : function() {
self.post.set({participation: false});
self.trigger('change');
self.set({"likes_count" : self.get("likes_count") - 1});
self.likes.trigger("change");
},
error: function(model, response) {
app.flashMessages.handleAjaxError(response);
}});
},
comment: function(text, options) {
var self = this;
options = options || {};
@@ -109,7 +63,7 @@ app.models.Post.Interactions = Backbone.Model.extend({
});
},
userCanReshare : function(){
userCanReshare: function() {
var isReshare = this.post.get("post_type") === "Reshare"
, rootExists = (isReshare ? this.post.get("root") : true)
, publicPost = this.post.get("public")

View File

@@ -0,0 +1,57 @@
// This class contains code extracted from interactions.js to factorize likes management between posts and comments
app.models.Post.LikeInteractions = Backbone.Model.extend({
initialize: function(options) {
this.likes = new app.collections.Likes(this.get("likes"), options);
this.post = options.post;
},
likesCount: function() {
return this.get("likes_count");
},
userLike: function() {
return this.likes.select(function(like) {
return like.get("author") && like.get("author").guid === app.currentUser.get("guid");
})[0];
},
toggleLike: function() {
if (this.userLike()) {
this.unlike();
} else {
this.like();
}
},
like: function() {
var self = this;
this.likes.create({}, {
success: function() {
self.post.set({participation: true});
self.trigger("change");
self.set({"likes_count": self.get("likes_count") + 1});
self.likes.trigger("change");
},
error: function(model, response) {
app.flashMessages.handleAjaxError(response);
}
});
},
unlike: function() {
var self = this;
this.userLike().destroy({
success: function() {
// TODO: If user unlikes a post and the last like of all comments, then set participation to false
self.post.set({participation: false});
self.trigger("change");
self.set({"likes_count": self.get("likes_count") - 1});
self.likes.trigger("change");
},
error: function(model, response) {
app.flashMessages.handleAjaxError(response);
}});
}
});

View File

@@ -6,7 +6,11 @@ app.views.Comment = app.views.Content.extend({
className : "comment media",
tooltipSelector: "time",
events : function() {
subviews: {
".likes-on-comment": "likesInfoView"
},
events: function() {
return _.extend({}, app.views.Content.prototype.events, {
"click .comment_delete": "destroyModel",
"click .comment_report": "report",
@@ -14,33 +18,40 @@ app.views.Comment = app.views.Content.extend({
});
},
initialize : function(options){
initialize: function(options) {
this.templateName = options.templateName || this.templateName;
this.model.interactions.on("change", this.render, this);
this.model.on("change", this.render, this);
},
presenter : function() {
presenter: function() {
return _.extend(this.defaultPresenter(), {
canRemove: this.canRemove(),
text: app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people"))
text: app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people")),
likesCount: this.model.attributes.likesCount,
userLike: this.model.interactions.userLike()
});
},
ownComment : function() {
ownComment: function() {
return app.currentUser.authenticated() && this.model.get("author").diaspora_id === app.currentUser.get("diaspora_id");
},
postOwner : function() {
postOwner: function() {
return app.currentUser.authenticated() && this.model.get("parent").author.diaspora_id === app.currentUser.get("diaspora_id");
},
canRemove : function() {
canRemove: function() {
return app.currentUser.authenticated() && (this.ownComment() || this.postOwner());
},
toggleLike: function(evt) {
if (evt) { evt.preventDefault(); }
this.model.toggleLike();
this.model.interactions.toggleLike();
},
likesInfoView: function() {
return new app.views.LikesInfo({model: this.model});
}
});

View File

@@ -7,7 +7,7 @@ app.views.StreamPost = app.views.Post.extend({
subviews : {
".feedback": "feedbackView",
".comments": "commentStreamView",
".likes": "likesInfoView",
".likes-on-post": "likesInfoView",
".reshares": "resharesInfoView",
".post-controls": "postControlsView",
".post-content": "postContentView",

View File

@@ -46,5 +46,8 @@
{{~/if~}}
</a>
</div>
<div class="likes likes-on-comment"> </div>
</div>
</div>

View File

@@ -37,7 +37,7 @@
{{#unless preview}}
<div class="feedback nsfw-hidden"> </div>
<div class="likes nsfw-hidden"> </div>
<div class="likes likes-on-post nsfw-hidden"> </div>
<div class="reshares nsfw-hidden"> </div>
<div class="comments nsfw-hidden"> </div>
{{/unless}}

View File

@@ -33,7 +33,7 @@ module Api
post = post_service.find!(params.require(:post_id))
raise ActiveRecord::RecordInvalid unless post.public? || private_read?
like_service.create(params[:post_id])
like_service.create_for_post(params[:post_id])
rescue ActiveRecord::RecordInvalid => e
if e.message == "Validation failed: Target has already been taken"
return render_error 409, "Like already exists"

View File

@@ -41,7 +41,12 @@ class LikesController < ApplicationController
end
def index
render json: like_service.find_for_post(params[:post_id])
like = if params[:post_id]
like_service.find_for_post(params[:post_id])
else
like_service.find_for_comment(params[:comment_id])
end
render json: like
.includes(author: :profile)
.as_api_response(:backbone)
end

View File

@@ -17,6 +17,10 @@ module User::SocialActions
end
end
def like_comment!(target, opts={})
Like::Generator.new(self, target).create!(opts)
end
def participate_in_poll!(target, answer, opts={})
PollParticipation::Generator.new(self, target, answer).create!(opts).tap do
update_or_create_participation!(target)

View File

@@ -8,7 +8,8 @@ class CommentPresenter < BasePresenter
text: message.plain_text_for_json,
author: author.as_api_response(:backbone),
created_at: created_at,
mentioned_people: mentioned_people.as_api_response(:backbone)
mentioned_people: mentioned_people.as_api_response(:backbone),
interactions: build_interactions_json
}
end
@@ -19,11 +20,25 @@ class CommentPresenter < BasePresenter
author: PersonPresenter.new(author).as_api_json,
created_at: created_at,
mentioned_people: build_mentioned_people_json,
reported: current_user.present? && reports.where(user: current_user).exists?
reported: current_user.present? && reports.where(user: current_user).exists?,
interactions: build_interactions_json
}
end
def build_interactions_json
{
likes: as_api(likes),
likes_count: likes_count
}
end
def build_mentioned_people_json
mentioned_people.map {|m| PersonPresenter.new(m).as_api_json }
end
def as_api(collection)
collection.includes(author: :profile).map {|element|
element.as_api_response(:backbone)
}
end
end

View File

@@ -14,8 +14,8 @@ class CommentService
post_service.find!(post_id).comments.for_a_stream
end
def find!(comment_guid)
Comment.find_by!(guid: comment_guid)
def find!(id_or_guid)
Comment.find_by!(comment_key(id_or_guid) => id_or_guid)
end
def destroy(comment_id)
@@ -45,6 +45,11 @@ class CommentService
attr_reader :user
# We can assume a guid is at least 16 characters long as we have guids set to hex(8) since we started using them.
def comment_key(id_or_guid)
id_or_guid.to_s.length < 16 ? :id : :guid
end
def post_service
@post_service ||= PostService.new(user)
end

View File

@@ -12,7 +12,8 @@ class LikeService
def create_for_comment(comment_id)
comment = comment_service.find!(comment_id)
user.like!(comment)
post_service.find!(comment.commentable_id) # checks implicit for visible posts
user.like_comment!(comment)
end
def destroy(like_id)
@@ -30,6 +31,13 @@ class LikeService
user ? likes.order(Arel.sql("author_id = #{user.person.id} DESC")) : likes
end
def find_for_comment(comment_id)
comment = comment_service.find!(comment_id)
post_service.find!(comment.post.id) # checks implicit for visible posts
likes = comment.likes
user ? likes.order(Arel.sql("author_id = #{user.person.id} DESC")) : likes
end
def unlike_post(post_id)
likes = post_service.find!(post_id).likes
likes = likes.order(Arel.sql("author_id = #{user.person.id} DESC"))
@@ -41,6 +49,17 @@ class LikeService
end
end
def unlike_comment(comment_id)
likes = comment_service.find!(comment_id).likes
likes = likes.order(Arel.sql("author_id = #{user.person.id} DESC"))
if !likes.empty? && user.owns?(likes[0])
user.retract(likes[0])
true
else
false
end
end
private
attr_reader :user

View File

@@ -101,9 +101,16 @@
"type": "array",
"items": { "$ref": "https://diaspora.software/api/v1/schema.json#/definitions/short_profile" }
},
"reported": { "type": "boolean" }
"reported": { "type": "boolean" },
"interactions": {
"type": "object",
"properties" : {
"likes" : { "$ref": "https://diaspora.software/api/v1/schema.json#/definitions/likes"},
"likes_count" : { "type": "integer"}
}
}
},
"required": ["guid", "created_at", "author", "body", "reported"],
"required": ["guid", "created_at", "author", "body", "reported", "interactions"],
"additionalProperties": false
}
},

View File

@@ -61,9 +61,9 @@ describe Api::V1::LikesController do
end
it "succeeds in getting post with likes" do
like_service(bob).create(@status.guid)
like_service(auth.user).create(@status.guid)
like_service(alice).create(@status.guid)
like_service(bob).create_for_post(@status.guid)
like_service(auth.user).create_for_post(@status.guid)
like_service(alice).create_for_post(@status.guid)
get(
api_v1_post_likes_path(post_id: @status.guid),
params: {access_token: access_token_minimum_scopes}
@@ -112,7 +112,7 @@ describe Api::V1::LikesController do
describe "#create" do
context "with right post id" do
it "succeeeds in liking post" do
it "succeeds in liking post" do
post(
api_v1_post_likes_path(post_id: @status.guid),
params: {access_token: access_token}
@@ -181,7 +181,7 @@ describe Api::V1::LikesController do
describe "#delete" do
before do
like_service.create(@status.guid)
like_service.create_for_post(@status.guid)
end
context "with right post id" do
@@ -225,7 +225,7 @@ describe Api::V1::LikesController do
context "with improper credentials" do
it "fails at unliking private post without private:read" do
like_service(auth_public_only.user).create(@private_status.guid)
like_service(auth_public_only.user).create_for_post(@private_status.guid)
delete(
api_v1_post_likes_path(post_id: @private_status.guid),
params: {access_token: access_token}
@@ -234,7 +234,7 @@ describe Api::V1::LikesController do
end
it "fails in unliking post without interactions" do
like_service(auth_minimum_scopes.user).create(@status.guid)
like_service(auth_minimum_scopes.user).create_for_post(@status.guid)
delete(
api_v1_post_likes_path(post_id: @status.guid),
params: {access_token: access_token_minimum_scopes}

View File

@@ -43,13 +43,15 @@ var factory = {
comment : function(overrides) {
var defaultAttrs = {
"created_at" : "2012-01-04T00:55:30Z",
"author" : this.author(),
"guid" : this.guid(),
"id" : this.id.next(),
"text" : "This is a comment!"
"created_at": "2012-01-04T00:55:30Z",
"author": this.author(),
"guid": this.guid(),
"id": this.id.next(),
"text": "This is a comment!"
};
overrides = overrides || {};
overrides.post = this.post();
return new app.models.Comment(_.extend(defaultAttrs, overrides));
},

View File

@@ -10,7 +10,7 @@ describe LikesPresenter do
to: "all"
)
bobs_like_service = LikeService.new(bob)
like = bobs_like_service.create(@status.guid)
like = bobs_like_service.create_for_post(@status.guid)
@presenter = LikesPresenter.new(like, bob)
end

View File

@@ -2,67 +2,129 @@
describe LikeService do
let(:post) { alice.post(:status_message, text: "hello", to: alice.aspects.first) }
let(:alice_comment) { CommentService.new(alice).create(post.id, "This is a wonderful post") }
let(:bobs_comment) { CommentService.new(bob).create(post.id, "My post was better than yours") }
describe "#create" do
describe "#create_for_post" do
it "creates a like on my own post" do
expect {
LikeService.new(alice).create(post.id)
LikeService.new(alice).create_for_post(post.id)
}.not_to raise_error
end
it "creates a like on a post of a contact" do
expect {
LikeService.new(bob).create(post.id)
LikeService.new(bob).create_for_post(post.id)
}.not_to raise_error
end
it "attaches the like to the post" do
like = LikeService.new(alice).create(post.id)
like = LikeService.new(alice).create_for_post(post.id)
expect(post.likes.first.id).to eq(like.id)
end
it "fails if the post does not exist" do
expect {
LikeService.new(bob).create("unknown id")
LikeService.new(bob).create_for_post("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if the user can't see the post" do
expect {
LikeService.new(eve).create(post.id)
LikeService.new(eve).create_for_post(post.id)
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if the user already liked the post" do
LikeService.new(alice).create(post.id)
LikeService.new(alice).create_for_post(post.id)
expect {
LikeService.new(alice).create(post.id)
LikeService.new(alice).create_for_post(post.id)
}.to raise_error ActiveRecord::RecordInvalid
end
end
describe "#create_for_comment" do
it "creates a like on a posts comment" do
expect {
LikeService.new(alice).create_for_comment(alice_comment.id)
}.not_to raise_error
end
it "creates a like on someone else comment" do
expect {
LikeService.new(alice).create_for_comment(bobs_comment.id)
}.not_to raise_error
end
it "attaches the like to the comment" do
like = LikeService.new(alice).create_for_comment(bobs_comment.id)
expect(bobs_comment.likes.first.id).to eq(like.id)
end
it "fails if comment does not exist" do
expect {
LikeService.new(alice).create_for_comment("unknown_id")
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if user cant see post and its comments" do
expect {
LikeService.new(eve).create_for_comment(bobs_comment.id)
}.to raise_error ActiveRecord::RecordNotFound
end
it "fails if user already liked the comment" do
LikeService.new(alice).create_for_comment(bobs_comment.id)
expect {
LikeService.new(alice).create_for_comment(bobs_comment.id)
}.to raise_error ActiveRecord::RecordInvalid
end
end
describe "#destroy" do
let(:like) { LikeService.new(bob).create(post.id) }
context "for post like" do
let(:like) { LikeService.new(bob).create_for_post(post.id) }
it "lets the user destroy their own like" do
result = LikeService.new(bob).destroy(like.id)
expect(result).to be_truthy
it "lets the user destroy their own like" do
result = LikeService.new(bob).destroy(like.id)
expect(result).to be_truthy
end
it "doesn't let the parent author destroy others likes" do
result = LikeService.new(alice).destroy(like.id)
expect(result).to be_falsey
end
it "doesn't let someone destroy others likes" do
result = LikeService.new(eve).destroy(like.id)
expect(result).to be_falsey
end
it "fails if the like doesn't exist" do
expect {
LikeService.new(bob).destroy("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
end
it "doesn't let the parent author destroy others likes" do
result = LikeService.new(alice).destroy(like.id)
expect(result).to be_falsey
end
context "for comment like" do
let(:like) { LikeService.new(bob).create_for_comment(alice_comment.id) }
it "doesn't let someone destroy others likes" do
result = LikeService.new(eve).destroy(like.id)
expect(result).to be_falsey
end
it "let the user destroy its own comment like" do
result = LikeService.new(bob).destroy(like.id)
expect(result).to be_truthy
end
it "fails if the like doesn't exist" do
expect {
LikeService.new(bob).destroy("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
it "doesn't let the parent author destroy other comment likes" do
result = LikeService.new(alice).destroy(like.id)
expect(result).to be_falsey
end
it "fails if the like doesn't exist" do
expect {
LikeService.new(alice).destroy("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
end
end
@@ -70,17 +132,17 @@ describe LikeService do
context "with user" do
it "returns likes for a public post" do
post = alice.post(:status_message, text: "hello", public: true)
like = LikeService.new(alice).create(post.id)
like = LikeService.new(alice).create_for_post(post.id)
expect(LikeService.new(eve).find_for_post(post.id)).to include(like)
end
it "returns likes for a visible private post" do
like = LikeService.new(alice).create(post.id)
like = LikeService.new(alice).create_for_post(post.id)
expect(LikeService.new(bob).find_for_post(post.id)).to include(like)
end
it "doesn't return likes for a private post the user can not see" do
LikeService.new(alice).create(post.id)
LikeService.new(alice).create_for_post(post.id)
expect {
LikeService.new(eve).find_for_post(post.id)
}.to raise_error ActiveRecord::RecordNotFound
@@ -88,7 +150,7 @@ describe LikeService do
it "returns the user's like first" do
post = alice.post(:status_message, text: "hello", public: true)
[alice, bob, eve].map {|user| LikeService.new(user).create(post.id) }
[alice, bob, eve].map {|user| LikeService.new(user).create_for_post(post.id) }
[alice, bob, eve].each do |user|
expect(
@@ -101,12 +163,12 @@ describe LikeService do
context "without user" do
it "returns likes for a public post" do
post = alice.post(:status_message, text: "hello", public: true)
like = LikeService.new(alice).create(post.id)
like = LikeService.new(alice).create_for_post(post.id)
expect(LikeService.new.find_for_post(post.id)).to include(like)
end
it "doesn't return likes a for private post" do
LikeService.new(alice).create(post.id)
LikeService.new(alice).create_for_post(post.id)
expect {
LikeService.new.find_for_post(post.id)
}.to raise_error Diaspora::NonPublic
@@ -115,15 +177,68 @@ describe LikeService do
it "returns all likes of a post" do
post = alice.post(:status_message, text: "hello", public: true)
likes = [alice, bob, eve].map {|user| LikeService.new(user).create(post.id) }
likes = [alice, bob, eve].map {|user| LikeService.new(user).create_for_post(post.id) }
expect(LikeService.new.find_for_post(post.id)).to match_array(likes)
end
end
describe "#find_for_comment" do
context "with user" do
it "returns likes for a public post comment" do
post = alice.post(:status_message, text: "hello", public: true)
comment = CommentService.new(bob).create(post.id, "Hello comment")
like = LikeService.new(alice).create_for_comment(comment.id)
expect(LikeService.new(eve).find_for_comment(comment.id)).to include(like)
end
it "returns likes for visible private post comments" do
comment = CommentService.new(bob).create(post.id, "Hello comment")
like = LikeService.new(alice).create_for_comment(comment.id)
expect(LikeService.new(bob).find_for_comment(comment.id)).to include(like)
end
it "doesn't return likes for a posts comment the user can not see" do
expect {
LikeService.new(eve).find_for_comment(alice_comment.id)
}.to raise_error ActiveRecord::RecordNotFound
end
it "returns the user's like first" do
post = alice.post(:status_message, text: "hello", public: true)
comment = CommentService.new(alice).create(post.id, "I like my own post")
[alice, bob, eve].map {|user| LikeService.new(user).create_for_comment(comment.id) }
[alice, bob, eve].each do |user|
expect(
LikeService.new(user).find_for_comment(comment.id).first.author.id
).to be user.person.id
end
end
end
context "without user" do
it "returns likes for a comment on a public post" do
post = alice.post(:status_message, text: "hello", public: true)
comment = CommentService.new(bob).create(post.id, "I like my own post")
like = LikeService.new(alice).create_for_comment(comment.id)
expect(
LikeService.new.find_for_comment(comment.id)
).to include(like)
end
it "doesn't return likes for a private post comment" do
LikeService.new(alice).create_for_comment(alice_comment.id)
expect {
LikeService.new.find_for_comment(alice_comment.id)
}.to raise_error Diaspora::NonPublic
end
end
end
describe "#unlike_post" do
before do
LikeService.new(alice).create(post.id)
LikeService.new(alice).create_for_post(post.id)
end
it "removes the like to the post" do
@@ -131,4 +246,16 @@ describe LikeService do
expect(post.likes.length).to eq(0)
end
end
describe "#unlike_comment" do
it "removes the like for a comment" do
comment = CommentService.new(alice).create(post.id, "I like my own post")
LikeService.new(alice).create_for_comment(comment.id)
expect(comment.likes.length).to eq(1)
LikeService.new(alice).unlike_comment(comment.id)
comment = CommentService.new(alice).find!(comment.id)
expect(comment.likes.length).to eq(0)
end
end
end