diff --git a/Changelog.md b/Changelog.md
index bd1fc24a7..33951d6b1 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -29,6 +29,7 @@ If so, please delete it since it will prevent the federation from working proper
* Use id as fallback when sorting posts [#7523](https://github.com/diaspora/diaspora/pull/7523)
* Remove no-posts-info when adding posts to the stream [#7523](https://github.com/diaspora/diaspora/pull/7523)
* Upgrade to rails 5.1 [#7514](https://github.com/diaspora/diaspora/pull/7514)
+* Refactoring single post view interactions [#7182](https://github.com/diaspora/diaspora/pull/7182)
## Bug fixes
diff --git a/app/assets/javascripts/app/models/post.js b/app/assets/javascripts/app/models/post.js
index c351ee787..b693d7b2d 100644
--- a/app/assets/javascripts/app/models/post.js
+++ b/app/assets/javascripts/app/models/post.js
@@ -48,12 +48,6 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
var body = this.get("text").trim()
, newlineIdx = body.indexOf("\n");
return (newlineIdx > 0 ) ? body.substr(newlineIdx+1, body.length) : "";
- },
-
- //returns a promise
- preloadOrFetch : function(){
- var action = app.hasPreload("post") ? this.set(app.parsePreload("post")) : this.fetch();
- return $.when(action);
}
}));
// @license-end
diff --git a/app/assets/javascripts/app/models/post/interactions.js b/app/assets/javascripts/app/models/post/interactions.js
index b3700b97d..5770b3291 100644
--- a/app/assets/javascripts/app/models/post/interactions.js
+++ b/app/assets/javascripts/app/models/post/interactions.js
@@ -3,10 +3,6 @@
//require ../post
app.models.Post.Interactions = Backbone.Model.extend({
- url : function(){
- return this.post.url() + "/interactions";
- },
-
initialize : function(options){
this.post = options.post;
this.comments = new app.collections.Comments(this.get("comments"), {post : this.post});
@@ -14,33 +10,16 @@ app.models.Post.Interactions = Backbone.Model.extend({
this.reshares = new app.collections.Reshares(this.get("reshares"), {post : this.post});
},
- parse : function(resp){
- this.comments.reset(resp.comments);
- this.likes.reset(resp.likes);
- this.reshares.reset(resp.reshares);
-
- var comments = this.comments
- , likes = this.likes
- , reshares = this.reshares;
-
- return {
- comments : comments,
- likes : likes,
- reshares : reshares,
- fetched : true
- };
- },
-
likesCount : function(){
- return this.get("fetched") ? this.likes.models.length : this.get("likes_count");
+ return this.get("likes_count");
},
resharesCount : function(){
- return this.get("fetched") ? this.reshares.models.length : this.get("reshares_count");
+ return this.get("reshares_count");
},
commentsCount : function(){
- return this.get("fetched") ? this.comments.models.length : this.get("comments_count");
+ return this.get("comments_count");
},
userLike : function(){
diff --git a/app/assets/javascripts/app/pages/single-post-viewer.js b/app/assets/javascripts/app/pages/single-post-viewer.js
index c5a30f1c9..f12f45a33 100644
--- a/app/assets/javascripts/app/pages/single-post-viewer.js
+++ b/app/assets/javascripts/app/pages/single-post-viewer.js
@@ -9,9 +9,8 @@ app.pages.SinglePostViewer = app.views.Base.extend({
},
initialize : function() {
- this.model = new app.models.Post({ id : gon.post.id });
- this.model.preloadOrFetch().done(_.bind(this.initViews, this));
- this.model.interactions.fetch(); //async, yo, might want to throttle this later.
+ this.model = new app.models.Post(gon.post);
+ this.initViews();
},
initViews : function() {
diff --git a/app/assets/javascripts/app/router.js b/app/assets/javascripts/app/router.js
index 287aef9a6..ab9e11a69 100644
--- a/app/assets/javascripts/app/router.js
+++ b/app/assets/javascripts/app/router.js
@@ -182,7 +182,7 @@ app.Router = Backbone.Router.extend({
},
singlePost: function(id) {
- this.renderPage(function() { return new app.pages.SinglePostViewer({id: id}); });
+ this.renderPage(function() { return new app.pages.SinglePostViewer({id: id, el: $("#container")}); });
},
spotlight: function() {
diff --git a/app/assets/javascripts/app/views/single-post-viewer/single_post_comment_stream.js b/app/assets/javascripts/app/views/single-post-viewer/single_post_comment_stream.js
index 49ddf11db..6981bdc3a 100644
--- a/app/assets/javascripts/app/views/single-post-viewer/single_post_comment_stream.js
+++ b/app/assets/javascripts/app/views/single-post-viewer/single_post_comment_stream.js
@@ -7,7 +7,9 @@ app.views.SinglePostCommentStream = app.views.CommentStream.extend({
this.CommentView = app.views.ExpandedComment;
$(window).on('hashchange',this.highlightPermalinkComment);
this.setupBindings();
- this.model.comments.on("reset", this.render, this);
+ this.model.comments.fetch({success: function() {
+ setTimeout(this.highlightPermalinkComment, 0);
+ }.bind(this)});
},
highlightPermalinkComment: function() {
@@ -17,14 +19,13 @@ app.views.SinglePostCommentStream = app.views.CommentStream.extend({
$(".highlighted").removeClass("highlighted");
element.addClass("highlighted");
var pos = element.offset().top - headerSize;
- window.scroll(0, pos);
+ $("html,body").animate({scrollTop: pos});
}
},
postRenderTemplate: function() {
app.views.CommentStream.prototype.postRenderTemplate.apply(this);
this.$(".new-comment-form-wrapper").removeClass("hidden");
- _.defer(this.highlightPermalinkComment);
},
presenter: function(){
diff --git a/app/assets/javascripts/app/views/single-post-viewer/single_post_interaction_counts.js b/app/assets/javascripts/app/views/single-post-viewer/single_post_interaction_counts.js
index 42348627c..abc4f5d9e 100644
--- a/app/assets/javascripts/app/views/single-post-viewer/single_post_interaction_counts.js
+++ b/app/assets/javascripts/app/views/single-post-viewer/single_post_interaction_counts.js
@@ -4,8 +4,15 @@ app.views.SinglePostInteractionCounts = app.views.Base.extend({
templateName: "single-post-viewer/single-post-interaction-counts",
tooltipSelector: ".avatar.micro",
+ events: {
+ "click #show-all-likes": "showAllLikes",
+ "click #show-all-reshares": "showAllReshares"
+ },
+
initialize: function() {
this.model.interactions.on("change", this.render, this);
+ this.model.interactions.likes.on("change", this.render, this);
+ this.model.interactions.reshares.on("change", this.render, this);
},
presenter: function() {
@@ -15,8 +22,28 @@ app.views.SinglePostInteractionCounts = app.views.Base.extend({
reshares: interactions.reshares.toJSON(),
commentsCount: interactions.commentsCount(),
likesCount: interactions.likesCount(),
- resharesCount: interactions.resharesCount()
+ resharesCount: interactions.resharesCount(),
+ showMoreLikes: interactions.likes.length < interactions.likesCount(),
+ showMoreReshares: interactions.reshares.length < interactions.resharesCount()
};
+ },
+
+ _showAll: function(interactionType, models) {
+ this.$("#show-all-" + interactionType).addClass("hidden");
+ this.$("#" + interactionType + " .loader").removeClass("hidden");
+ models.fetch({success: function() {
+ models.trigger("change");
+ }});
+ },
+
+ showAllLikes: function(evt) {
+ evt.preventDefault();
+ this._showAll("likes", this.model.interactions.likes);
+ },
+
+ showAllReshares: function(evt) {
+ evt.preventDefault();
+ this._showAll("reshares", this.model.interactions.reshares);
}
});
// @license-end
diff --git a/app/assets/stylesheets/single-post-view.scss b/app/assets/stylesheets/single-post-view.scss
index 15fdbcbde..ceaabc4fa 100644
--- a/app/assets/stylesheets/single-post-view.scss
+++ b/app/assets/stylesheets/single-post-view.scss
@@ -152,5 +152,16 @@
.interaction-avatars {
overflow: hidden;
+
+ .author-name:focus,
+ .author-name:hover {
+ text-decoration: none;
+ }
+
+ .loader {
+ height: $line-height-computed;
+ vertical-align: text-bottom;
+ width: $line-height-computed;
+ }
}
}
diff --git a/app/assets/templates/single-post-viewer/single-post-interaction-counts_tpl.jst.hbs b/app/assets/templates/single-post-viewer/single-post-interaction-counts_tpl.jst.hbs
index 221274487..dcf8cdd31 100644
--- a/app/assets/templates/single-post-viewer/single-post-interaction-counts_tpl.jst.hbs
+++ b/app/assets/templates/single-post-viewer/single-post-interaction-counts_tpl.jst.hbs
@@ -10,6 +10,12 @@
{{{personImage this "small" "micro"}}}
{{/linkToAuthor}}
{{/each}}
+ {{#if showMoreReshares}}
+
+ {{t "show_all"}}
+ {{/if}}
{{/if}}
@@ -25,6 +31,12 @@
{{{personImage this "small" "micro"}}}
{{/linkToAuthor}}
{{/each}}
+ {{#if showMoreLikes}}
+
+ {{t "show_all"}}
+ {{/if}}
{{/if}}
diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb
index 8b5386d2b..9d6b3258c 100644
--- a/app/controllers/posts_controller.rb
+++ b/app/controllers/posts_controller.rb
@@ -22,11 +22,11 @@ class PostsController < ApplicationController
presenter = PostPresenter.new(post, current_user)
respond_to do |format|
format.html do
- gon.post = presenter
+ gon.post = presenter.with_initial_interactions
render locals: {post: presenter}
end
format.mobile { render locals: {post: post} }
- format.json { render json: presenter }
+ format.json { render json: presenter.with_interactions }
end
end
@@ -39,16 +39,6 @@ class PostsController < ApplicationController
head :not_found
end
- def interactions
- respond_to do |format|
- format.json {
- post = post_service.find!(params[:id])
- render json: PostInteractionPresenter.new(post, current_user)
- }
- format.any { head :not_acceptable }
- end
- end
-
def mentionable
respond_to do |format|
format.json {
diff --git a/app/controllers/reshares_controller.rb b/app/controllers/reshares_controller.rb
index 846091398..d38f49f6a 100644
--- a/app/controllers/reshares_controller.rb
+++ b/app/controllers/reshares_controller.rb
@@ -7,7 +7,7 @@ class ResharesController < ApplicationController
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
render plain: I18n.t("reshares.create.error"), status: 422
else
- render json: ExtremePostPresenter.new(reshare, current_user), status: 201
+ render json: PostPresenter.new(reshare, current_user).with_interactions, status: 201
end
def index
diff --git a/app/presenters/extreme_post_presenter.rb b/app/presenters/extreme_post_presenter.rb
deleted file mode 100644
index c35b54088..000000000
--- a/app/presenters/extreme_post_presenter.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-#this file should go away, hence the name that is so full of lulz
-#post interactions should probably be a decorator, and used in very few places... maybe?
-class ExtremePostPresenter
- def initialize(post, current_user)
- @post = post
- @current_user = current_user
- end
-
- def as_json(options={})
- post = PostPresenter.new(@post, @current_user)
- interactions = PostInteractionPresenter.new(@post, @current_user)
- post.as_json.merge!(:interactions => interactions.as_json)
- end
-end
\ No newline at end of file
diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb
index 86064fbf9..a1187fba9 100644
--- a/app/presenters/post_presenter.rb
+++ b/app/presenters/post_presenter.rb
@@ -14,6 +14,20 @@ class PostPresenter < BasePresenter
.merge(non_directly_retrieved_attributes)
end
+ def with_interactions
+ interactions = PostInteractionPresenter.new(@post, current_user)
+ as_json.merge!(interactions: interactions.as_json)
+ end
+
+ def with_initial_interactions
+ as_json.tap do |post|
+ post[:interactions].merge!(
+ likes: LikeService.new(current_user).find_for_post(@post.id).limit(30).as_api_response(:backbone),
+ reshares: ReshareService.new(current_user).find_for_post(@post.id).limit(30).as_api_response(:backbone)
+ )
+ end
+ end
+
def metas_attributes
{
keywords: {name: "keywords", content: comma_separated_tags},
diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml
index 7c5071762..8f4ed6953 100644
--- a/config/locales/javascript/javascript.en.yml
+++ b/config/locales/javascript/javascript.en.yml
@@ -28,6 +28,7 @@ en:
comma: ","
edit: "Edit"
no_results: "No results found"
+ show_all: "Show all"
admins:
dashboard:
diff --git a/config/routes.rb b/config/routes.rb
index b7d4c69ab..82d9afc91 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -29,7 +29,6 @@ Rails.application.routes.draw do
resources :posts, only: %i(show destroy) do
member do
- get :interactions
get :mentionable
end
diff --git a/spec/controllers/jasmine_fixtures/posts_spec.rb b/spec/controllers/jasmine_fixtures/posts_spec.rb
new file mode 100644
index 000000000..aed5d118a
--- /dev/null
+++ b/spec/controllers/jasmine_fixtures/posts_spec.rb
@@ -0,0 +1,11 @@
+require "spec_helper"
+
+describe PostsController, type: :controller do
+ describe "#show" do
+ it "generates the post_json fixture", fixture: true do
+ post = alice.post(:status_message, text: "hello world", public: true)
+ get :show, params: {id: post.id}, format: :json
+ save_fixture(response.body, "post_json")
+ end
+ end
+end
diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb
index 28216a97e..2c5f72f92 100644
--- a/spec/controllers/posts_controller_spec.rb
+++ b/spec/controllers/posts_controller_spec.rb
@@ -129,38 +129,6 @@ describe PostsController, type: :controller do
end
end
- describe "#interactions" do
- context "user not signed in" do
- it "returns a 401 for private posts and format json" do
- get :interactions, params: {id: post.id}, format: :json
- expect(response.status).to eq(401)
- expect(JSON.parse(response.body)["error"]).to eq(I18n.t("devise.failure.unauthenticated"))
- end
-
- it "returns a 406 for private posts and format html" do
- get :interactions, params: {id: post.id}
- expect(response.status).to eq(406)
- end
- end
-
- context "user signed in" do
- before do
- sign_in alice
- end
-
- it "shows interactions of a post as json" do
- get :interactions, params: {id: post.id}, format: :json
- expect(response.body).to eq(PostInteractionPresenter.new(post, alice).to_json)
- end
-
- it "returns a 406 for format html" do
- sign_in alice
- get :interactions, params: {id: post.id}
- expect(response.status).to eq(406)
- end
- end
- end
-
describe "#mentionable" do
context "with a user signed in" do
before do
diff --git a/spec/javascripts/app/pages/single-post-viewer_spec.js b/spec/javascripts/app/pages/single-post-viewer_spec.js
index df8f9c43c..c1db51394 100644
--- a/spec/javascripts/app/pages/single-post-viewer_spec.js
+++ b/spec/javascripts/app/pages/single-post-viewer_spec.js
@@ -1,6 +1,7 @@
describe("app.pages.SinglePostViewer", function(){
beforeEach(function() {
- window.gon={};gon.post = {id: 42};
+ window.gon = {};
+ gon.post = $.parseJSON(spec.readFixture("post_json"));
this.view = new app.pages.SinglePostViewer();
});
diff --git a/spec/javascripts/app/views/single-post-view/single_post_comment_stream_spec.js b/spec/javascripts/app/views/single-post-view/single_post_comment_stream_spec.js
index 3e32ca3ee..28c80225d 100644
--- a/spec/javascripts/app/views/single-post-view/single_post_comment_stream_spec.js
+++ b/spec/javascripts/app/views/single-post-view/single_post_comment_stream_spec.js
@@ -9,14 +9,6 @@ describe("app.views.SinglePostCommentStream", function() {
expect(this.view.CommentView).toBe(app.views.ExpandedComment);
});
- it("calls render when the comments collection has been resetted", function() {
- spyOn(app.views.SinglePostCommentStream.prototype, "render");
- this.view.initialize();
- expect(app.views.SinglePostCommentStream.prototype.render).not.toHaveBeenCalled();
- this.post.comments.reset();
- expect(app.views.SinglePostCommentStream.prototype.render).toHaveBeenCalled();
- });
-
it("calls setupBindings", function() {
spyOn(app.views.SinglePostCommentStream.prototype, "setupBindings");
this.view.initialize();
diff --git a/spec/javascripts/app/views/single-post-view/single_post_interaction_counts_spec.js b/spec/javascripts/app/views/single-post-view/single_post_interaction_counts_spec.js
index 35abf7afe..0ff6959bc 100644
--- a/spec/javascripts/app/views/single-post-view/single_post_interaction_counts_spec.js
+++ b/spec/javascripts/app/views/single-post-view/single_post_interaction_counts_spec.js
@@ -1,6 +1,6 @@
describe("app.views.SinglePostInteractionCounts", function() {
beforeEach(function() {
- this.post = factory.post();
+ this.post = factory.postWithInteractions();
this.view = new app.views.SinglePostInteractionCounts({model: this.post});
});
@@ -12,5 +12,151 @@ describe("app.views.SinglePostInteractionCounts", function() {
this.post.interactions.trigger("change");
expect(app.views.SinglePostInteractionCounts.prototype.render).toHaveBeenCalled();
});
+
+ it("calls render when the likes change", function() {
+ spyOn(app.views.SinglePostInteractionCounts.prototype, "render");
+ this.view.initialize();
+ expect(app.views.SinglePostInteractionCounts.prototype.render).not.toHaveBeenCalled();
+ this.post.interactions.likes.trigger("change");
+ expect(app.views.SinglePostInteractionCounts.prototype.render).toHaveBeenCalled();
+ });
+
+ it("calls render when the reshares change", function() {
+ spyOn(app.views.SinglePostInteractionCounts.prototype, "render");
+ this.view.initialize();
+ expect(app.views.SinglePostInteractionCounts.prototype.render).not.toHaveBeenCalled();
+ this.post.interactions.reshares.trigger("change");
+ expect(app.views.SinglePostInteractionCounts.prototype.render).toHaveBeenCalled();
+ });
+ });
+
+ describe("render", function() {
+ it("doesn't show a #show-all-likes link if there are no additional likes", function() {
+ this.view.render();
+ expect(this.view.$("#show-all-likes").length).toBe(0);
+ });
+
+ it("shows a #show-all-likes link if there are additional likes", function() {
+ this.view.model.interactions.set("likes_count", this.view.model.interactions.likes.length + 1);
+ this.view.render();
+ expect(this.view.$("#show-all-likes").length).toBe(1);
+ });
+
+ it("doesn't show a #show-all-reshares link if there are no additional reshares", function() {
+ this.view.render();
+ expect(this.view.$("#show-all-reshares").length).toBe(0);
+ });
+
+ it("shows a #show-all-reshares link if there are additional reshares", function() {
+ this.view.model.interactions.set("reshares_count", this.view.model.interactions.reshares.length + 1);
+ this.view.render();
+ expect(this.view.$("#show-all-reshares").length).toBe(1);
+ });
+ });
+
+ describe("showAllLikes", function() {
+ it("is called when clicking #show-all-likes", function() {
+ spyOn(this.view, "showAllLikes");
+ this.view.delegateEvents();
+ this.view.model.interactions.set("likes_count", this.view.model.interactions.likes.length + 1);
+ this.view.render();
+ expect(this.view.showAllLikes).not.toHaveBeenCalled();
+ this.view.$("#show-all-likes").click();
+ expect(this.view.showAllLikes).toHaveBeenCalled();
+ });
+
+ it("calls _showAll", function() {
+ spyOn(this.view, "_showAll");
+ this.view.showAllLikes($.Event());
+ expect(this.view._showAll).toHaveBeenCalledWith("likes", this.view.model.interactions.likes);
+ });
+ });
+
+ describe("showAllReshares", function() {
+ it("is called when clicking #show-all-reshares", function() {
+ spyOn(this.view, "showAllReshares");
+ this.view.delegateEvents();
+ this.view.model.interactions.set("reshares_count", this.view.model.interactions.reshares.length + 1);
+ this.view.render();
+ expect(this.view.showAllReshares).not.toHaveBeenCalled();
+ this.view.$("#show-all-reshares").click();
+ expect(this.view.showAllReshares).toHaveBeenCalled();
+ });
+
+ it("calls _showAll", function() {
+ spyOn(this.view, "_showAll");
+ this.view.showAllReshares($.Event());
+ expect(this.view._showAll).toHaveBeenCalledWith("reshares", this.view.model.interactions.reshares);
+ });
+ });
+
+ describe("_showAll", function() {
+ beforeEach(function() {
+ this.view.model.interactions.set("likes_count", this.view.model.interactions.likes.length + 1);
+ this.view.model.interactions.set("reshares_count", this.view.model.interactions.reshares.length + 1);
+ this.view.render();
+ });
+
+ context("with likes", function() {
+ it("hides the #show-all-likes link", function() {
+ expect(this.view.$("#show-all-likes")).not.toHaveClass("hidden");
+ expect(this.view.$("#show-all-reshares")).not.toHaveClass("hidden");
+ this.view._showAll("likes", this.view.model.interactions.likes);
+ expect(this.view.$("#show-all-likes")).toHaveClass("hidden");
+ expect(this.view.$("#show-all-reshares")).not.toHaveClass("hidden");
+ });
+
+ it("shows the likes loader", function() {
+ expect(this.view.$("#likes .loader")).toHaveClass("hidden");
+ expect(this.view.$("#reshares .loader")).toHaveClass("hidden");
+ this.view._showAll("likes", this.view.model.interactions.likes);
+ expect(this.view.$("#likes .loader")).not.toHaveClass("hidden");
+ expect(this.view.$("#reshares .loader")).toHaveClass("hidden");
+ });
+
+ it("calls #fetch on the model", function() {
+ spyOn(this.view.model.interactions.likes, "fetch");
+ this.view._showAll("likes", this.view.model.interactions.likes);
+ expect(this.view.model.interactions.likes.fetch).toHaveBeenCalled();
+ });
+
+ it("triggers 'change' after a successfull fetch", function() {
+ spyOn(this.view.model.interactions.likes, "trigger");
+ this.view._showAll("likes", this.view.model.interactions.likes);
+ jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: "{\"id\": 1}"});
+ expect(this.view.model.interactions.likes.trigger).toHaveBeenCalledWith("change");
+ });
+ });
+
+ context("with reshares", function() {
+ it("hides the #show-all-reshares link", function() {
+ expect(this.view.$("#show-all-likes")).not.toHaveClass("hidden");
+ expect(this.view.$("#show-all-reshares")).not.toHaveClass("hidden");
+ this.view._showAll("reshares", this.view.model.interactions.reshares);
+ expect(this.view.$("#show-all-likes")).not.toHaveClass("hidden");
+ expect(this.view.$("#show-all-reshares")).toHaveClass("hidden");
+ });
+
+ it("shows the reshares loader", function() {
+ expect(this.view.$("#likes .loader")).toHaveClass("hidden");
+ expect(this.view.$("#reshares .loader")).toHaveClass("hidden");
+ this.view._showAll("reshares", this.view.model.interactions.reshares);
+ expect(this.view.$("#likes .loader")).toHaveClass("hidden");
+ expect(this.view.$("#reshares .loader")).not.toHaveClass("hidden");
+ });
+
+ it("calls #fetch on the model", function() {
+ spyOn(this.view.model.interactions.reshares, "fetch");
+ this.view._showAll("reshares", this.view.model.interactions.reshares);
+ expect(this.view.model.interactions.reshares.fetch).toHaveBeenCalled();
+ });
+
+ it("triggers 'change' after a successfull fetch", function() {
+ spyOn(this.view.model.interactions.reshares, "trigger");
+ this.view._showAll("reshares", this.view.model.interactions.reshares);
+ jasmine.Ajax.requests.mostRecent().respondWith({status: 200, responseText: "{\"id\": 1}"});
+ expect(this.view.model.interactions.reshares.trigger).toHaveBeenCalledWith("change");
+ });
+ });
});
});
diff --git a/spec/javascripts/jasmine_helpers/factory.js b/spec/javascripts/jasmine_helpers/factory.js
index 551fe87bc..ba97e5b71 100644
--- a/spec/javascripts/jasmine_helpers/factory.js
+++ b/spec/javascripts/jasmine_helpers/factory.js
@@ -21,6 +21,16 @@ var factory = {
return _.extend(defaultAttrs, overrides);
},
+ reshare: function(overrides) {
+ var defaultAttrs = {
+ "created_at": "2012-01-04T00:55:30Z",
+ "author": this.author(),
+ "guid": this.guid(),
+ "id": this.id.next()
+ };
+ return _.extend(defaultAttrs, overrides);
+ },
+
aspectMembershipAttrs: function(overrides) {
var id = this.id.next();
var defaultAttrs = {
@@ -207,6 +217,24 @@ var factory = {
return new app.models.Post(_.extend(defaultAttrs, overrides));
},
+ postWithInteractions: function(overrides) {
+ var likes = _.range(10).map(function() { return factory.like(); });
+ var reshares = _.range(15).map(function() { return factory.reshare(); });
+ var comments = _.range(20).map(function() { return factory.comment(); });
+ var defaultAttrs = _.extend(factory.postAttrs(), {
+ "author": this.author(),
+ "interactions": {
+ "reshares_count": 15,
+ "likes_count": 10,
+ "comments_count": 20,
+ "comments": comments,
+ "likes": likes,
+ "reshares": reshares
+ }
+ });
+ return new app.models.Post(_.extend(defaultAttrs, overrides));
+ },
+
statusMessage : function(overrides){
//intentionally doesn't have an author to mirror creation process, maybe we should change the creation process
return new app.models.StatusMessage(_.extend(factory.postAttrs(), overrides));
diff --git a/spec/presenters/post_presenter_spec.rb b/spec/presenters/post_presenter_spec.rb
index 952da0a2c..4110cedeb 100644
--- a/spec/presenters/post_presenter_spec.rb
+++ b/spec/presenters/post_presenter_spec.rb
@@ -1,44 +1,93 @@
describe PostPresenter do
- before do
- @sm = FactoryGirl.create(:status_message, public: true)
- @sm_with_poll = FactoryGirl.create(:status_message_with_poll, public: true)
- @presenter = PostPresenter.new(@sm, bob)
- @unauthenticated_presenter = PostPresenter.new(@sm)
- end
+ let(:status_message) { FactoryGirl.create(:status_message, public: true) }
+ let(:status_message_with_poll) { FactoryGirl.create(:status_message_with_poll, public: true) }
+ let(:presenter) { PostPresenter.new(status_message, bob) }
+ let(:unauthenticated_presenter) { PostPresenter.new(status_message) }
it "takes a post and an optional user" do
- expect(@presenter).not_to be_nil
+ expect(presenter).not_to be_nil
end
describe "#as_json" do
it "works with a user" do
- expect(@presenter.as_json).to be_a Hash
+ expect(presenter.as_json).to be_a Hash
end
it "works without a user" do
- expect(@unauthenticated_presenter.as_json).to be_a Hash
+ expect(unauthenticated_presenter.as_json).to be_a Hash
+ end
+ end
+
+ context "post with interactions" do
+ before do
+ bob.like!(status_message)
+ bob.reshare!(status_message)
+ end
+
+ describe "#with_interactions" do
+ it "works with a user" do
+ post_hash = presenter.with_interactions
+ expect(post_hash).to be_a Hash
+ expect(post_hash[:interactions]).to eq PostInteractionPresenter.new(status_message, bob).as_json
+ end
+
+ it "works without a user" do
+ post_hash = unauthenticated_presenter.with_interactions
+ expect(post_hash).to be_a Hash
+ expect(post_hash[:interactions]).to eq PostInteractionPresenter.new(status_message, nil).as_json
+ end
+ end
+
+ describe "#with_initial_interactions" do
+ it "works with a user" do
+ post_hash = presenter.with_initial_interactions
+ expect(post_hash).to be_a Hash
+ expect(post_hash[:interactions][:likes]).to eq(
+ LikeService.new(bob).find_for_post(status_message.id).as_api_response(:backbone)
+ )
+ expect(post_hash[:interactions][:reshares]).to eq(
+ ReshareService.new(bob).find_for_post(status_message.id).as_api_response(:backbone)
+ )
+ end
+
+ it "works without a user" do
+ post_hash = unauthenticated_presenter.with_initial_interactions
+ expect(post_hash).to be_a Hash
+ expect(post_hash[:interactions][:likes]).to eq(
+ LikeService.new.find_for_post(status_message.id).as_api_response(:backbone)
+ )
+ expect(post_hash[:interactions][:reshares]).to eq(
+ ReshareService.new.find_for_post(status_message.id).as_api_response(:backbone)
+ )
+ end
end
end
describe "#user_like" do
+ before do
+ bob.like!(status_message)
+ end
+
it "includes the users like" do
- bob.like!(@sm)
- expect(@presenter.send(:user_like)).to be_present
+ expect(presenter.send(:user_like)).to be_present
end
it "is nil if the user is not authenticated" do
- expect(@unauthenticated_presenter.send(:user_like)).to be_nil
+ expect(unauthenticated_presenter.send(:user_like)).to be_nil
end
end
describe "#user_reshare" do
+ before do
+ bob.reshare!(status_message)
+ end
+
it "includes the users reshare" do
- bob.reshare!(@sm)
- expect(@presenter.send(:user_reshare)).to be_present
+ expect(presenter.send(:user_reshare)).to be_present
end
it "is nil if the user is not authenticated" do
- expect(@unauthenticated_presenter.send(:user_reshare)).to be_nil
+ expect(unauthenticated_presenter.send(:user_reshare)).to be_nil
end
end
@@ -67,23 +116,23 @@ describe PostPresenter do
it "delegates to message.title" do
message = double(present?: true)
expect(message).to receive(:title)
- @presenter.post = double(message: message)
- @presenter.send(:title)
+ presenter.post = double(message: message)
+ presenter.send(:title)
end
end
context "with posts without text" do
it "displays a messaage with the post class" do
- @sm = double(message: double(present?: false), author: bob.person, author_name: bob.person.name)
- @presenter.post = @sm
- expect(@presenter.send(:title)).to eq("A post from #{@sm.author.name}")
+ sm = double(message: double(present?: false), author: bob.person, author_name: bob.person.name)
+ presenter.post = sm
+ expect(presenter.send(:title)).to eq("A post from #{sm.author.name}")
end
end
end
describe "#poll" do
it "works without a user" do
- presenter = PostPresenter.new(@sm_with_poll)
+ presenter = PostPresenter.new(status_message_with_poll)
expect(presenter.as_json).to be_a(Hash)
end
end
@@ -134,15 +183,15 @@ describe PostPresenter do
describe "#build_open_graph_cache" do
it "returns a dummy og cache if the og cache is missing" do
- expect(@presenter.build_open_graph_cache.image).to be_nil
+ expect(presenter.build_open_graph_cache.image).to be_nil
end
context "with an open graph cache" do
it "delegates to as_api_response" do
og_cache = double("open_graph_cache")
expect(og_cache).to receive(:as_api_response).with(:backbone)
- @presenter.post = double(open_graph_cache: og_cache)
- @presenter.send(:build_open_graph_cache)
+ presenter.post = double(open_graph_cache: og_cache)
+ presenter.send(:build_open_graph_cache)
end
it "returns the open graph cache data" do