From 8faedd574d77830c8af08e89b92432fc4161db31 Mon Sep 17 00:00:00 2001 From: Steffen van Bergerem Date: Tue, 30 Aug 2016 16:21:10 +0200 Subject: [PATCH] Move post controls to a separate view --- .../app/views/post_controls_view.js | 78 +++++++ .../app/views/stream_post_views.js | 86 ++------ .../templates/post-controls_tpl.jst.hbs | 24 +++ .../templates/stream-element_tpl.jst.hbs | 29 +-- .../app/views/post_controls_view_spec.js | 200 ++++++++++++++++++ 5 files changed, 317 insertions(+), 100 deletions(-) create mode 100644 app/assets/javascripts/app/views/post_controls_view.js create mode 100644 app/assets/templates/post-controls_tpl.jst.hbs create mode 100644 spec/javascripts/app/views/post_controls_view_spec.js diff --git a/app/assets/javascripts/app/views/post_controls_view.js b/app/assets/javascripts/app/views/post_controls_view.js new file mode 100644 index 000000000..796e8565a --- /dev/null +++ b/app/assets/javascripts/app/views/post_controls_view.js @@ -0,0 +1,78 @@ +// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + +app.views.PostControls = app.views.Base.extend({ + templateName: "post-controls", + className: "control-icons", + + events: { + "click .remove_post": "destroyModel", + "click .hide_post": "hidePost", + "click .post_report": "report", + "click .block_user": "blockUser", + "click .create_participation": "createParticipation", + "click .destroy_participation": "destroyParticipation" + }, + + tooltipSelector: [".post_report", + ".block_user", + ".delete", + ".create_participation", + ".destroy_participation"].join(", "), + + initialize: function(opts) { + this.model.bind("change", this.render, this); + this.post = opts.post; + }, + + presenter: function() { + return _.extend(this.defaultPresenter(), { + authorIsCurrentUser: app.currentUser.isAuthorOf(this.model) + }); + }, + + blockUser: function(evt) { + if (evt) { evt.preventDefault(); } + if (!confirm(Diaspora.I18n.t("ignore_user"))) { return; } + + this.model.blockAuthor().fail(function() { + app.flashMessages.error(Diaspora.I18n.t("ignore_failed")); + }); + }, + + hidePost: function(evt) { + if (evt) { evt.preventDefault(); } + if (!confirm(Diaspora.I18n.t("confirm_dialog"))) { return; } + + var self = this; + $.ajax({ + url: Routes.shareVisibility(42), + type: "PUT", + data: { + /* eslint-disable camelcase */ + post_id: this.model.id + /* eslint-enable camelcase */ + } + }).done(function() { + self.post.remove(); + }).fail(function() { + app.flashMessages.error(Diaspora.I18n.t("hide_post_failed")); + }); + }, + + createParticipation: function(evt) { + if (evt) { evt.preventDefault(); } + var that = this; + $.post(Routes.postParticipation(this.model.get("id")), {}, function() { + that.model.set({participation: true}); + }); + }, + + destroyParticipation: function(evt) { + if (evt) { evt.preventDefault(); } + var that = this; + $.post(Routes.postParticipation(this.model.get("id")), {_method: "delete"}, function() { + that.model.set({participation: false}); + }); + } +}); +// @license-end diff --git a/app/assets/javascripts/app/views/stream_post_views.js b/app/assets/javascripts/app/views/stream_post_views.js index b298cd0ad..60812f6b9 100644 --- a/app/assets/javascripts/app/views/stream_post_views.js +++ b/app/assets/javascripts/app/views/stream_post_views.js @@ -5,38 +5,26 @@ app.views.StreamPost = app.views.Post.extend({ className : "stream_element loaded", subviews : { - ".feedback" : "feedbackView", - ".likes" : "likesInfoView", - ".reshares" : "resharesInfoView", - ".comments" : "commentStreamView", - ".post-content" : "postContentView", - ".oembed" : "oEmbedView", - ".opengraph" : "openGraphView", - ".poll" : "pollView", - ".status-message-location" : "postLocationStreamView" + ".feedback": "feedbackView", + ".comments": "commentStreamView", + ".likes": "likesInfoView", + ".reshares": "resharesInfoView", + ".post-controls": "postControlsView", + ".post-content": "postContentView", + ".oembed": "oEmbedView", + ".opengraph": "openGraphView", + ".poll": "pollView", + ".status-message-location": "postLocationStreamView" }, events: { "click .focus_comment_textarea": "focusCommentTextarea", "click .show_nsfw_post": "removeNsfwShield", - "click .toggle_nsfw_state": "toggleNsfwState", - - "click .remove_post": "destroyModel", - "click .hide_post": "hidePost", - "click .post_report": "report", - "click .block_user": "blockUser", - - "click .create_participation": "createParticipation", - "click .destroy_participation": "destroyParticipation" + "click .toggle_nsfw_state": "toggleNsfwState" }, tooltipSelector : [".timeago", ".post_scope", - ".post_report", - ".block_user", - ".delete", - ".create_participation", - ".destroy_participation", ".permalink"].join(", "), initialize : function(){ @@ -51,6 +39,9 @@ app.views.StreamPost = app.views.Post.extend({ this.pollView = new app.views.Poll({model : this.model}); }, + postControlsView: function() { + return new app.views.PostControls({model: this.model, post: this}); + }, likesInfoView : function(){ return new app.views.LikesInfo({model : this.model}); @@ -87,60 +78,12 @@ app.views.StreamPost = app.views.Post.extend({ app.currentUser.toggleNsfwState(); }, - - blockUser: function(evt){ - if(evt) { evt.preventDefault(); } - if(!confirm(Diaspora.I18n.t("ignore_user"))) { return } - - this.model.blockAuthor() - .fail(function() { - app.flashMessages.error(Diaspora.I18n.t("ignore_failed")); - }); - }, - remove : function() { $(this.el).slideUp(400, _.bind(function(){this.$el.remove()}, this)); app.stream.remove(this.model); return this; }, - hidePost : function(evt) { - if(evt) { evt.preventDefault(); } - if(!confirm(Diaspora.I18n.t("confirm_dialog"))) { return } - - var self = this; - $.ajax({ - url : "/share_visibilities/42", - type : "PUT", - data : { - post_id : this.model.id - } - }).done(function() { - self.remove(); - }) - .fail(function() { - app.flashMessages.error(Diaspora.I18n.t("hide_post_failed")); - }); - }, - - createParticipation: function (evt) { - if(evt) { evt.preventDefault(); } - var that = this; - $.post(Routes.postParticipation(this.model.get("id")), {}, function () { - that.model.set({participation: true}); - that.render(); - }); - }, - - destroyParticipation: function (evt) { - if(evt) { evt.preventDefault(); } - var that = this; - $.post(Routes.postParticipation(this.model.get("id")), { _method: "delete" }, function () { - that.model.set({participation: false}); - that.render(); - }); - }, - focusCommentTextarea: function(evt){ evt.preventDefault(); this.$(".new-comment-form-wrapper").removeClass("hidden"); @@ -148,6 +91,5 @@ app.views.StreamPost = app.views.Post.extend({ return this; } - }); // @license-end diff --git a/app/assets/templates/post-controls_tpl.jst.hbs b/app/assets/templates/post-controls_tpl.jst.hbs new file mode 100644 index 000000000..560f366f4 --- /dev/null +++ b/app/assets/templates/post-controls_tpl.jst.hbs @@ -0,0 +1,24 @@ +{{#if authorIsCurrentUser}} + + + +{{else}} + + + + + + + {{#if participation}} + + + + {{else}} + + + + {{/if}} + + + +{{/if}} diff --git a/app/assets/templates/stream-element_tpl.jst.hbs b/app/assets/templates/stream-element_tpl.jst.hbs index 2520c1e67..2c65c427a 100644 --- a/app/assets/templates/stream-element_tpl.jst.hbs +++ b/app/assets/templates/stream-element_tpl.jst.hbs @@ -7,34 +7,7 @@
{{#if loggedIn}} -
- {{#unless preview}} - {{#if authorIsCurrentUser}} - - - - {{else}} - - - - - - - {{#if participation}} - - - - {{else}} - - - - {{/if}} - - - - {{/if}} - {{/unless}} -
+
{{/if}}
diff --git a/spec/javascripts/app/views/post_controls_view_spec.js b/spec/javascripts/app/views/post_controls_view_spec.js new file mode 100644 index 000000000..01a5385cd --- /dev/null +++ b/spec/javascripts/app/views/post_controls_view_spec.js @@ -0,0 +1,200 @@ +describe("app.views.PostControls", function() { + describe("render", function() { + beforeEach(function() { + this.model = factory.post(); + this.view = new app.views.PostControls({model: this.model}); + }); + + context("in a post of the current user", function() { + beforeEach(function() { + app.currentUser = new app.models.User(this.model.attributes.author); + this.view.render(); + }); + + it("shows a delete button", function() { + expect(this.view.$(".delete.remove_post").length).toBe(1); + }); + + it("doesn't show a report button", function() { + expect(this.view.$(".post_report").length).toBe(0); + }); + + it("doesn't show an ignore button", function() { + expect(this.view.$(".block_user").length).toBe(0); + }); + + it("doesn't show participation buttons", function() { + expect(this.view.$(".create_participation").length).toBe(0); + expect(this.view.$(".destroy_participation").length).toBe(0); + }); + + it("doesn't show a hide button", function() { + expect(this.view.$(".delete.hide_post").length).toBe(0); + }); + }); + + context("in a post of another user", function() { + beforeEach(function() { + this.view.render(); + }); + + it("doesn't show a delete button", function() { + expect(this.view.$(".delete.remove_post").length).toBe(0); + }); + + it("shows a report button", function() { + expect(this.view.$(".post_report").length).toBe(1); + }); + + it("shows an ignore button", function() { + expect(this.view.$(".block_user").length).toBe(1); + }); + + it("shows a create participation button", function() { + expect(this.view.$(".create_participation").length).toBe(1); + expect(this.view.$(".destroy_participation").length).toBe(0); + }); + + it("shows a destroy participation button if the user participated", function() { + this.model.set({participation: true}); + this.view.render(); + expect(this.view.$(".create_participation").length).toBe(0); + expect(this.view.$(".destroy_participation").length).toBe(1); + }); + + it("shows a hide button", function() { + expect(this.view.$(".delete.hide_post").length).toBe(1); + }); + }); + }); + + describe("events", function() { + beforeEach(function() { + this.model = factory.post(); + }); + + it("calls destroyModel when removing a post", function() { + spyOn(app.views.PostControls.prototype, "destroyModel"); + app.currentUser = new app.models.User(this.model.attributes.author); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + this.view.$(".remove_post.delete").click(); + expect(app.views.PostControls.prototype.destroyModel).toHaveBeenCalled(); + }); + + it("calls hidePost when hiding a post", function() { + spyOn(app.views.PostControls.prototype, "hidePost"); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + this.view.$(".hide_post.delete").click(); + expect(app.views.PostControls.prototype.hidePost).toHaveBeenCalled(); + }); + + it("calls report when reporting a post", function() { + spyOn(app.views.PostControls.prototype, "report"); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + this.view.$(".post_report").click(); + expect(app.views.PostControls.prototype.report).toHaveBeenCalled(); + }); + + it("calls blockUser when blocking the user", function() { + spyOn(app.views.PostControls.prototype, "blockUser"); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + this.view.$(".block_user").click(); + expect(app.views.PostControls.prototype.blockUser).toHaveBeenCalled(); + }); + + it("calls createParticipation when creating a participation", function() { + spyOn(app.views.PostControls.prototype, "createParticipation"); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + this.view.$(".create_participation").click(); + expect(app.views.PostControls.prototype.createParticipation).toHaveBeenCalled(); + }); + + it("calls destroyParticipation when destroying a participation", function() { + spyOn(app.views.PostControls.prototype, "destroyParticipation"); + this.model.set({participation: true}); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + this.view.$(".destroy_participation").click(); + expect(app.views.PostControls.prototype.destroyParticipation).toHaveBeenCalled(); + }); + }); + + describe("initialize", function() { + it("rerenders the view when the model has been changed", function() { + spyOn(app.views.PostControls.prototype, "render"); + this.model = factory.post(); + this.view = new app.views.PostControls({model: this.model}); + expect(app.views.PostControls.prototype.render).not.toHaveBeenCalled(); + this.model.trigger("change"); + expect(app.views.PostControls.prototype.render).toHaveBeenCalled(); + }); + }); + + describe("blockUser", function() { + beforeEach(function() { + spyOn(window, "confirm").and.returnValue(true); + this.model = factory.post(); + this.view = new app.views.PostControls({model: this.model}); + this.view.render(); + }); + + it("asks for a confirmation", function() { + this.view.blockUser(); + expect(window.confirm).toHaveBeenCalledWith(Diaspora.I18n.t("ignore_user")); + }); + + it("calls blockAuthor", function() { + spyOn(this.model, "blockAuthor").and.callThrough(); + this.view.blockUser(); + expect(this.model.blockAuthor).toHaveBeenCalled(); + }); + + it("shows a flash message when errors occur", function() { + spyOn(app.flashMessages, "error"); + this.view.blockUser(); + jasmine.Ajax.requests.mostRecent().respondWith({status: 422}); + expect(app.flashMessages.error).toHaveBeenCalledWith(Diaspora.I18n.t("ignore_failed")); + }); + }); + + describe("hidePost", function() { + beforeEach(function() { + spyOn(window, "confirm").and.returnValue(true); + this.postView = {remove: function() { return; }}; + this.model = factory.post(); + this.view = new app.views.PostControls({model: this.model, post: this.postView}); + this.view.render(); + }); + + it("asks for a confirmation", function() { + this.view.hidePost(); + expect(window.confirm).toHaveBeenCalledWith(Diaspora.I18n.t("confirm_dialog")); + }); + + it("sends an ajax request with the correct post id", function() { + this.view.hidePost(); + expect(jasmine.Ajax.requests.mostRecent().url).toBe(Routes.shareVisibility(42)); + expect(jasmine.Ajax.requests.mostRecent().data().post_id).toEqual(["" + this.model.get("id")]); + expect(jasmine.Ajax.requests.mostRecent().method).toBe("PUT"); + }); + + it("removes the post on success", function() { + spyOn(this.view.post, "remove"); + this.view.hidePost(); + jasmine.Ajax.requests.mostRecent().respondWith({status: 204}); + expect(this.view.post.remove).toHaveBeenCalled(); + }); + + it("shows a flash message when errors occur", function() { + spyOn(app.flashMessages, "error"); + this.view.hidePost(); + jasmine.Ajax.requests.mostRecent().respondWith({status: 422}); + expect(app.flashMessages.error).toHaveBeenCalledWith(Diaspora.I18n.t("hide_post_failed")); + }); + }); +});