Comments expansion refactoring

If you look at comment expand process with javascript debugger, you
will notice that at first comments get added to existing stream ("add"
event handler of model.comments gets launched because of model.comments.fetch).
Then the comment stream gets empty and then filled by the postRenderTemplate
handler.

This patch removes comments rerendering on the expansion. The fetching process is
changed so that older comments are added to the correct place, so the order
looks nice without rerendering. Thus, unnecessary job of rerendering is avoided.
This commit is contained in:
cmrd Senya 2015-09-16 22:26:58 +03:00
parent 8deef544dc
commit 8e6df0b1e3
No known key found for this signature in database
GPG key ID: 5FCC5BA680E67BFE
3 changed files with 56 additions and 39 deletions

View file

@ -21,16 +21,10 @@ app.views.CommentStream = app.views.Base.extend({
setupBindings: function() { setupBindings: function() {
this.model.comments.bind('add', this.appendComment, this); this.model.comments.bind('add', this.appendComment, this);
this.model.bind("commentsExpanded", this.storeTextareaValue, this);
this.model.bind("commentsExpanded", this.render, this);
}, },
postRenderTemplate : function() { postRenderTemplate : function() {
this.model.comments.each(this.appendComment, this); this.model.comments.each(this.appendComment, this);
// add autoexpanders to new comment textarea
this.$("textarea").val(this.textareaValue);
autosize.update(this.$("textarea"));
}, },
presenter: function(){ presenter: function(){
@ -62,34 +56,55 @@ app.views.CommentStream = app.views.Base.extend({
} }
}, },
_insertPoint: 0, // An index of the comment added in the last call of this.appendComment
// This adjusts this._insertPoint according to timestamp value
_moveInsertPoint: function(timestamp, commentBlocks) {
if (commentBlocks.length === 0) {
this._insertPoint = 0;
return;
}
if (this._insertPoint > commentBlocks.length) {
this._insertPoint = commentBlocks.length;
}
while (this._insertPoint > 0 && timestamp < commentBlocks.eq(this._insertPoint - 1).find("time").attr("datetime")) {
this._insertPoint--;
}
while (this._insertPoint < commentBlocks.length &&
timestamp > commentBlocks.eq(this._insertPoint).find("time").attr("datetime")) {
this._insertPoint++;
}
},
appendComment: function(comment) { appendComment: function(comment) {
// Set the post as the comment's parent, so we can check // Set the post as the comment's parent, so we can check
// on post ownership in the Comment view. // on post ownership in the Comment view.
comment.set({parent : this.model.toJSON()}); comment.set({parent : this.model.toJSON()});
this.$(".comments").append(new app.views.Comment({ var commentHtml = new app.views.Comment({model: comment}).render().el;
model: comment var commentBlocks = this.$(".comments div.comment.media");
}).render().el); this._moveInsertPoint(comment.get("created_at"), commentBlocks);
if (this._insertPoint === commentBlocks.length) {
this.$(".comments").append(commentHtml);
} else {
commentBlocks.eq(this._insertPoint).before(commentHtml);
}
this._insertPoint++;
}, },
commentTextareaFocused: function(){ commentTextareaFocused: function(){
this.$("form").removeClass('hidden').addClass("open"); this.$("form").removeClass('hidden').addClass("open");
}, },
storeTextareaValue: function(){
this.textareaValue = this.$('textarea').val();
},
expandComments: function(evt){ expandComments: function(evt){
if(evt){ evt.preventDefault(); } if(evt){ evt.preventDefault(); }
var self = this; var self = this;
this.model.comments.fetch({ this.model.comments.fetch({
success : function(resp){ success : function(resp){
self.model.set({ self.$("div.comment.show_comments").addClass("hidden");
comments : resp.models,
all_comments_loaded : true
});
self.model.trigger("commentsExpanded", self); self.model.trigger("commentsExpanded", self);
} }

View file

@ -1,12 +1,10 @@
{{#unless all_comments_loaded}} <div class="show_comments comment {{#unless showExpandCommentsLink}} hidden {{/unless}}">
<div class="show_comments comment {{#unless showExpandCommentsLink}} hidden {{/unless}}">
<div class="media"> <div class="media">
<a href="/posts/{{id}}#comments" class="toggle_post_comments"> <a href="/posts/{{id}}#comments" class="toggle_post_comments">
{{t "stream.more_comments" count=moreCommentsCount}} {{t "stream.more_comments" count=moreCommentsCount}}
</a> </a>
</div> </div>
</div> </div>
{{/unless}}
<div class="comments"> </div> <div class="comments"> </div>

View file

@ -5,20 +5,11 @@ describe("app.views.CommentStream", function(){
}); });
describe("binds", function() { describe("binds", function() {
it("re-renders on a commentsExpanded trigger", function(){ it("calls appendComment on insertion to the comments collection", function() {
spyOn(this.view, "render"); spyOn(this.view, "appendComment");
this.view.setupBindings(); this.view.setupBindings();
this.view.model.trigger("commentsExpanded"); this.view.model.comments.push(factory.comment());
expect(this.view.render).toHaveBeenCalled(); expect(this.view.appendComment).toHaveBeenCalled();
});
});
describe("postRenderTemplate", function(){
it("autoResizes the new comment textarea", function(){
spyOn(window.autosize, "update");
this.view.postRenderTemplate();
expect(window.autosize.update).toHaveBeenCalled();
expect(window.autosize.update.calls.mostRecent().args[0].selector).toBe("textarea");
}); });
}); });
@ -79,10 +70,23 @@ describe("app.views.CommentStream", function(){
this.view.appendComment(comment); this.view.appendComment(comment);
expect(comment.set).toHaveBeenCalled(); expect(comment.set).toHaveBeenCalled();
}); });
it("sorts comments in the right order", function() {
this.view.render();
this.view.appendComment(factory.comment({"created_at": new Date(2000).toJSON(), "text": "2"}));
this.view.appendComment(factory.comment({"created_at": new Date(4000).toJSON(), "text": "4"}));
this.view.appendComment(factory.comment({"created_at": new Date(5000).toJSON(), "text": "5"}));
this.view.appendComment(factory.comment({"created_at": new Date(6000).toJSON(), "text": "6"}));
this.view.appendComment(factory.comment({"created_at": new Date(1000).toJSON(), "text": "1"}));
this.view.appendComment(factory.comment({"created_at": new Date(3000).toJSON(), "text": "3"}));
expect(this.view.$(".comments div.comment.media").length).toEqual(6);
expect(this.view.$(".comments div.comment.media div.comment-content p").text()).toEqual("123456");
});
}); });
describe("expandComments", function() { describe("expandComments", function() {
it("refills the comment textbox on success", function() { it("doesn't drop the comment textbox value on success", function() {
this.view.render(); this.view.render();
this.view.$("textarea").val("great post!"); this.view.$("textarea").val("great post!");
this.view.expandComments(); this.view.expandComments();