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:
parent
82ff57a750
commit
8d6548b610
20 changed files with 348 additions and 169 deletions
|
|
@ -12,12 +12,17 @@ app.collections.Comments = Backbone.Collection.extend({
|
||||||
|
|
||||||
make : function(text) {
|
make : function(text) {
|
||||||
var self = this;
|
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({}, {
|
var deferred = comment.save({}, {
|
||||||
url: "/posts/"+ this.post.id +"/comments",
|
url: "/posts/"+ this.post.id +"/comments",
|
||||||
success: function() {
|
success: function() {
|
||||||
comment.set({author: app.currentUser.toJSON(), parent: self.post });
|
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);
|
self.add(comment);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ app.collections.Likes = Backbone.Collection.extend({
|
||||||
model: app.models.Like,
|
model: app.models.Like,
|
||||||
|
|
||||||
initialize : function(models, options) {
|
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
|
// 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
|
// @license-end
|
||||||
|
|
|
||||||
|
|
@ -3,51 +3,15 @@
|
||||||
app.models.Comment = Backbone.Model.extend({
|
app.models.Comment = Backbone.Model.extend({
|
||||||
urlRoot: "/comments",
|
urlRoot: "/comments",
|
||||||
|
|
||||||
initialize: function() {
|
initialize: function(model, options) {
|
||||||
this.likes = new app.collections.Likes(this.get("likes"), {comment: this});
|
options = options || {};
|
||||||
},
|
this.post = model.post || options.post || this.collection.post;
|
||||||
|
this.interactions = new app.models.Post.LikeInteractions(
|
||||||
// Copied from Post.Interaction. To be merged in an "interactable" class once comments can be commented too
|
_.extend({comment: this, post: this.post}, this.get("interactions"))
|
||||||
likesCount: function() {
|
);
|
||||||
return this.get("likes_count");
|
this.likes = this.interactions.likes;
|
||||||
},
|
this.likesCount = this.attributes.likes_count;
|
||||||
|
this.userLike = this.interactions.userLike();
|
||||||
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");
|
|
||||||
}});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// @license-end
|
// @license-end
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,29 @@
|
||||||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
|
// @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({
|
app.models.Post.Interactions = app.models.Post.LikeInteractions.extend({
|
||||||
initialize : function(options){
|
initialize: function(options) {
|
||||||
|
app.models.Post.LikeInteractions.prototype.initialize.apply(this, arguments);
|
||||||
this.post = options.post;
|
this.post = options.post;
|
||||||
this.comments = new app.collections.Comments(this.get("comments"), {post : this.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.reshares = new app.collections.Reshares(this.get("reshares"), {post : this.post});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
likesCount : function(){
|
resharesCount: function() {
|
||||||
return this.get("likes_count");
|
|
||||||
},
|
|
||||||
|
|
||||||
resharesCount : function(){
|
|
||||||
return this.get("reshares_count");
|
return this.get("reshares_count");
|
||||||
},
|
},
|
||||||
|
|
||||||
commentsCount : function(){
|
commentsCount: function() {
|
||||||
return this.get("comments_count");
|
return this.get("comments_count");
|
||||||
},
|
},
|
||||||
|
|
||||||
userLike : function(){
|
userReshare: function() {
|
||||||
return this.likes.select(function(like){
|
|
||||||
return like.get("author") && like.get("author").guid === app.currentUser.get("guid");
|
|
||||||
})[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
userReshare : function(){
|
|
||||||
return this.reshares.select(function(reshare){
|
return this.reshares.select(function(reshare){
|
||||||
return reshare.get("author") && reshare.get("author").guid === app.currentUser.get("guid");
|
return reshare.get("author") && reshare.get("author").guid === app.currentUser.get("guid");
|
||||||
})[0];
|
})[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) {
|
comment: function(text, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
options = options || {};
|
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"
|
var isReshare = this.post.get("post_type") === "Reshare"
|
||||||
, rootExists = (isReshare ? this.post.get("root") : true)
|
, rootExists = (isReshare ? this.post.get("root") : true)
|
||||||
, publicPost = this.post.get("public")
|
, publicPost = this.post.get("public")
|
||||||
|
|
|
||||||
57
app/assets/javascripts/app/models/post/like_interactions.js
Normal file
57
app/assets/javascripts/app/models/post/like_interactions.js
Normal 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);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -6,7 +6,11 @@ app.views.Comment = app.views.Content.extend({
|
||||||
className : "comment media",
|
className : "comment media",
|
||||||
tooltipSelector: "time",
|
tooltipSelector: "time",
|
||||||
|
|
||||||
events : function() {
|
subviews: {
|
||||||
|
".likes-on-comment": "likesInfoView"
|
||||||
|
},
|
||||||
|
|
||||||
|
events: function() {
|
||||||
return _.extend({}, app.views.Content.prototype.events, {
|
return _.extend({}, app.views.Content.prototype.events, {
|
||||||
"click .comment_delete": "destroyModel",
|
"click .comment_delete": "destroyModel",
|
||||||
"click .comment_report": "report",
|
"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.templateName = options.templateName || this.templateName;
|
||||||
|
this.model.interactions.on("change", this.render, this);
|
||||||
this.model.on("change", this.render, this);
|
this.model.on("change", this.render, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
presenter : function() {
|
presenter: function() {
|
||||||
return _.extend(this.defaultPresenter(), {
|
return _.extend(this.defaultPresenter(), {
|
||||||
canRemove: this.canRemove(),
|
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");
|
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");
|
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());
|
return app.currentUser.authenticated() && (this.ownComment() || this.postOwner());
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleLike: function(evt) {
|
toggleLike: function(evt) {
|
||||||
if (evt) { evt.preventDefault(); }
|
if (evt) { evt.preventDefault(); }
|
||||||
this.model.toggleLike();
|
this.model.interactions.toggleLike();
|
||||||
|
},
|
||||||
|
|
||||||
|
likesInfoView: function() {
|
||||||
|
return new app.views.LikesInfo({model: this.model});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ app.views.StreamPost = app.views.Post.extend({
|
||||||
subviews : {
|
subviews : {
|
||||||
".feedback": "feedbackView",
|
".feedback": "feedbackView",
|
||||||
".comments": "commentStreamView",
|
".comments": "commentStreamView",
|
||||||
".likes": "likesInfoView",
|
".likes-on-post": "likesInfoView",
|
||||||
".reshares": "resharesInfoView",
|
".reshares": "resharesInfoView",
|
||||||
".post-controls": "postControlsView",
|
".post-controls": "postControlsView",
|
||||||
".post-content": "postContentView",
|
".post-content": "postContentView",
|
||||||
|
|
|
||||||
|
|
@ -46,5 +46,8 @@
|
||||||
{{~/if~}}
|
{{~/if~}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="likes likes-on-comment"> </div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
{{#unless preview}}
|
{{#unless preview}}
|
||||||
<div class="feedback nsfw-hidden"> </div>
|
<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="reshares nsfw-hidden"> </div>
|
||||||
<div class="comments nsfw-hidden"> </div>
|
<div class="comments nsfw-hidden"> </div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ module Api
|
||||||
post = post_service.find!(params.require(:post_id))
|
post = post_service.find!(params.require(:post_id))
|
||||||
raise ActiveRecord::RecordInvalid unless post.public? || private_read?
|
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
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
if e.message == "Validation failed: Target has already been taken"
|
if e.message == "Validation failed: Target has already been taken"
|
||||||
return render_error 409, "Like already exists"
|
return render_error 409, "Like already exists"
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,12 @@ class LikesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
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)
|
.includes(author: :profile)
|
||||||
.as_api_response(:backbone)
|
.as_api_response(:backbone)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ module User::SocialActions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def like_comment!(target, opts={})
|
||||||
|
Like::Generator.new(self, target).create!(opts)
|
||||||
|
end
|
||||||
|
|
||||||
def participate_in_poll!(target, answer, opts={})
|
def participate_in_poll!(target, answer, opts={})
|
||||||
PollParticipation::Generator.new(self, target, answer).create!(opts).tap do
|
PollParticipation::Generator.new(self, target, answer).create!(opts).tap do
|
||||||
update_or_create_participation!(target)
|
update_or_create_participation!(target)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ class CommentPresenter < BasePresenter
|
||||||
text: message.plain_text_for_json,
|
text: message.plain_text_for_json,
|
||||||
author: author.as_api_response(:backbone),
|
author: author.as_api_response(:backbone),
|
||||||
created_at: created_at,
|
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
|
end
|
||||||
|
|
||||||
|
|
@ -19,11 +20,25 @@ class CommentPresenter < BasePresenter
|
||||||
author: PersonPresenter.new(author).as_api_json,
|
author: PersonPresenter.new(author).as_api_json,
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
mentioned_people: build_mentioned_people_json,
|
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
|
end
|
||||||
|
|
||||||
def build_mentioned_people_json
|
def build_mentioned_people_json
|
||||||
mentioned_people.map {|m| PersonPresenter.new(m).as_api_json }
|
mentioned_people.map {|m| PersonPresenter.new(m).as_api_json }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def as_api(collection)
|
||||||
|
collection.includes(author: :profile).map {|element|
|
||||||
|
element.as_api_response(:backbone)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ class CommentService
|
||||||
post_service.find!(post_id).comments.for_a_stream
|
post_service.find!(post_id).comments.for_a_stream
|
||||||
end
|
end
|
||||||
|
|
||||||
def find!(comment_guid)
|
def find!(id_or_guid)
|
||||||
Comment.find_by!(guid: comment_guid)
|
Comment.find_by!(comment_key(id_or_guid) => id_or_guid)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy(comment_id)
|
def destroy(comment_id)
|
||||||
|
|
@ -45,6 +45,11 @@ class CommentService
|
||||||
|
|
||||||
attr_reader :user
|
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
|
def post_service
|
||||||
@post_service ||= PostService.new(user)
|
@post_service ||= PostService.new(user)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ class LikeService
|
||||||
|
|
||||||
def create_for_comment(comment_id)
|
def create_for_comment(comment_id)
|
||||||
comment = comment_service.find!(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
|
end
|
||||||
|
|
||||||
def destroy(like_id)
|
def destroy(like_id)
|
||||||
|
|
@ -30,6 +31,13 @@ class LikeService
|
||||||
user ? likes.order(Arel.sql("author_id = #{user.person.id} DESC")) : likes
|
user ? likes.order(Arel.sql("author_id = #{user.person.id} DESC")) : likes
|
||||||
end
|
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)
|
def unlike_post(post_id)
|
||||||
likes = post_service.find!(post_id).likes
|
likes = post_service.find!(post_id).likes
|
||||||
likes = likes.order(Arel.sql("author_id = #{user.person.id} DESC"))
|
likes = likes.order(Arel.sql("author_id = #{user.person.id} DESC"))
|
||||||
|
|
@ -41,6 +49,17 @@ class LikeService
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
attr_reader :user
|
attr_reader :user
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,16 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "$ref": "https://diaspora.software/api/v1/schema.json#/definitions/short_profile" }
|
"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
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,9 @@ describe Api::V1::LikesController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "succeeds in getting post with likes" do
|
it "succeeds in getting post with likes" do
|
||||||
like_service(bob).create(@status.guid)
|
like_service(bob).create_for_post(@status.guid)
|
||||||
like_service(auth.user).create(@status.guid)
|
like_service(auth.user).create_for_post(@status.guid)
|
||||||
like_service(alice).create(@status.guid)
|
like_service(alice).create_for_post(@status.guid)
|
||||||
get(
|
get(
|
||||||
api_v1_post_likes_path(post_id: @status.guid),
|
api_v1_post_likes_path(post_id: @status.guid),
|
||||||
params: {access_token: access_token_minimum_scopes}
|
params: {access_token: access_token_minimum_scopes}
|
||||||
|
|
@ -112,7 +112,7 @@ describe Api::V1::LikesController do
|
||||||
|
|
||||||
describe "#create" do
|
describe "#create" do
|
||||||
context "with right post id" do
|
context "with right post id" do
|
||||||
it "succeeeds in liking post" do
|
it "succeeds in liking post" do
|
||||||
post(
|
post(
|
||||||
api_v1_post_likes_path(post_id: @status.guid),
|
api_v1_post_likes_path(post_id: @status.guid),
|
||||||
params: {access_token: access_token}
|
params: {access_token: access_token}
|
||||||
|
|
@ -181,7 +181,7 @@ describe Api::V1::LikesController do
|
||||||
|
|
||||||
describe "#delete" do
|
describe "#delete" do
|
||||||
before do
|
before do
|
||||||
like_service.create(@status.guid)
|
like_service.create_for_post(@status.guid)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with right post id" do
|
context "with right post id" do
|
||||||
|
|
@ -225,7 +225,7 @@ describe Api::V1::LikesController do
|
||||||
|
|
||||||
context "with improper credentials" do
|
context "with improper credentials" do
|
||||||
it "fails at unliking private post without private:read" 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(
|
delete(
|
||||||
api_v1_post_likes_path(post_id: @private_status.guid),
|
api_v1_post_likes_path(post_id: @private_status.guid),
|
||||||
params: {access_token: access_token}
|
params: {access_token: access_token}
|
||||||
|
|
@ -234,7 +234,7 @@ describe Api::V1::LikesController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails in unliking post without interactions" do
|
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(
|
delete(
|
||||||
api_v1_post_likes_path(post_id: @status.guid),
|
api_v1_post_likes_path(post_id: @status.guid),
|
||||||
params: {access_token: access_token_minimum_scopes}
|
params: {access_token: access_token_minimum_scopes}
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,15 @@ var factory = {
|
||||||
|
|
||||||
comment : function(overrides) {
|
comment : function(overrides) {
|
||||||
var defaultAttrs = {
|
var defaultAttrs = {
|
||||||
"created_at" : "2012-01-04T00:55:30Z",
|
"created_at": "2012-01-04T00:55:30Z",
|
||||||
"author" : this.author(),
|
"author": this.author(),
|
||||||
"guid" : this.guid(),
|
"guid": this.guid(),
|
||||||
"id" : this.id.next(),
|
"id": this.id.next(),
|
||||||
"text" : "This is a comment!"
|
"text": "This is a comment!"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
overrides = overrides || {};
|
||||||
|
overrides.post = this.post();
|
||||||
return new app.models.Comment(_.extend(defaultAttrs, overrides));
|
return new app.models.Comment(_.extend(defaultAttrs, overrides));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ describe LikesPresenter do
|
||||||
to: "all"
|
to: "all"
|
||||||
)
|
)
|
||||||
bobs_like_service = LikeService.new(bob)
|
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)
|
@presenter = LikesPresenter.new(like, bob)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,67 +2,129 @@
|
||||||
|
|
||||||
describe LikeService do
|
describe LikeService do
|
||||||
let(:post) { alice.post(:status_message, text: "hello", to: alice.aspects.first) }
|
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
|
it "creates a like on my own post" do
|
||||||
expect {
|
expect {
|
||||||
LikeService.new(alice).create(post.id)
|
LikeService.new(alice).create_for_post(post.id)
|
||||||
}.not_to raise_error
|
}.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a like on a post of a contact" do
|
it "creates a like on a post of a contact" do
|
||||||
expect {
|
expect {
|
||||||
LikeService.new(bob).create(post.id)
|
LikeService.new(bob).create_for_post(post.id)
|
||||||
}.not_to raise_error
|
}.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "attaches the like to the post" do
|
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)
|
expect(post.likes.first.id).to eq(like.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails if the post does not exist" do
|
it "fails if the post does not exist" do
|
||||||
expect {
|
expect {
|
||||||
LikeService.new(bob).create("unknown id")
|
LikeService.new(bob).create_for_post("unknown id")
|
||||||
}.to raise_error ActiveRecord::RecordNotFound
|
}.to raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails if the user can't see the post" do
|
it "fails if the user can't see the post" do
|
||||||
expect {
|
expect {
|
||||||
LikeService.new(eve).create(post.id)
|
LikeService.new(eve).create_for_post(post.id)
|
||||||
}.to raise_error ActiveRecord::RecordNotFound
|
}.to raise_error ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails if the user already liked the post" do
|
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 {
|
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
|
}.to raise_error ActiveRecord::RecordInvalid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#destroy" do
|
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
|
it "lets the user destroy their own like" do
|
||||||
result = LikeService.new(bob).destroy(like.id)
|
result = LikeService.new(bob).destroy(like.id)
|
||||||
expect(result).to be_truthy
|
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
|
end
|
||||||
|
|
||||||
it "doesn't let the parent author destroy others likes" do
|
context "for comment like" do
|
||||||
result = LikeService.new(alice).destroy(like.id)
|
let(:like) { LikeService.new(bob).create_for_comment(alice_comment.id) }
|
||||||
expect(result).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't let someone destroy others likes" do
|
it "let the user destroy its own comment like" do
|
||||||
result = LikeService.new(eve).destroy(like.id)
|
result = LikeService.new(bob).destroy(like.id)
|
||||||
expect(result).to be_falsey
|
expect(result).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
it "fails if the like doesn't exist" do
|
it "doesn't let the parent author destroy other comment likes" do
|
||||||
expect {
|
result = LikeService.new(alice).destroy(like.id)
|
||||||
LikeService.new(bob).destroy("unknown id")
|
expect(result).to be_falsey
|
||||||
}.to raise_error ActiveRecord::RecordNotFound
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -70,17 +132,17 @@ describe LikeService do
|
||||||
context "with user" do
|
context "with user" do
|
||||||
it "returns likes for a public post" do
|
it "returns likes for a public post" do
|
||||||
post = alice.post(:status_message, text: "hello", public: true)
|
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)
|
expect(LikeService.new(eve).find_for_post(post.id)).to include(like)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns likes for a visible private post" do
|
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)
|
expect(LikeService.new(bob).find_for_post(post.id)).to include(like)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't return likes for a private post the user can not see" do
|
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 {
|
expect {
|
||||||
LikeService.new(eve).find_for_post(post.id)
|
LikeService.new(eve).find_for_post(post.id)
|
||||||
}.to raise_error ActiveRecord::RecordNotFound
|
}.to raise_error ActiveRecord::RecordNotFound
|
||||||
|
|
@ -88,7 +150,7 @@ describe LikeService do
|
||||||
|
|
||||||
it "returns the user's like first" do
|
it "returns the user's like first" do
|
||||||
post = alice.post(:status_message, text: "hello", public: true)
|
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|
|
[alice, bob, eve].each do |user|
|
||||||
expect(
|
expect(
|
||||||
|
|
@ -101,12 +163,12 @@ describe LikeService do
|
||||||
context "without user" do
|
context "without user" do
|
||||||
it "returns likes for a public post" do
|
it "returns likes for a public post" do
|
||||||
post = alice.post(:status_message, text: "hello", public: true)
|
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)
|
expect(LikeService.new.find_for_post(post.id)).to include(like)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't return likes a for private post" do
|
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 {
|
expect {
|
||||||
LikeService.new.find_for_post(post.id)
|
LikeService.new.find_for_post(post.id)
|
||||||
}.to raise_error Diaspora::NonPublic
|
}.to raise_error Diaspora::NonPublic
|
||||||
|
|
@ -115,15 +177,68 @@ describe LikeService do
|
||||||
|
|
||||||
it "returns all likes of a post" do
|
it "returns all likes of a post" do
|
||||||
post = alice.post(:status_message, text: "hello", public: true)
|
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)
|
expect(LikeService.new.find_for_post(post.id)).to match_array(likes)
|
||||||
end
|
end
|
||||||
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
|
describe "#unlike_post" do
|
||||||
before do
|
before do
|
||||||
LikeService.new(alice).create(post.id)
|
LikeService.new(alice).create_for_post(post.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "removes the like to the post" do
|
it "removes the like to the post" do
|
||||||
|
|
@ -131,4 +246,16 @@ describe LikeService do
|
||||||
expect(post.likes.length).to eq(0)
|
expect(post.likes.length).to eq(0)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue