From a307b60dd2bd3282cfb1c8329d841780cf3ac97e Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Wed, 18 Apr 2012 12:34:09 -0700 Subject: [PATCH] DC DG extract infininte scroll to a mixin --- app/assets/javascripts/app/views.js | 66 ++++++++++++++++- .../javascripts/app/views/stream_view.js | 70 +++---------------- .../javascripts/app/views/stream_view_spec.js | 11 ++- 3 files changed, 85 insertions(+), 62 deletions(-) diff --git a/app/assets/javascripts/app/views.js b/app/assets/javascripts/app/views.js index 0e6f2aaf0..fc7329390 100644 --- a/app/assets/javascripts/app/views.js +++ b/app/assets/javascripts/app/views.js @@ -21,7 +21,8 @@ app.views.Base = Backbone.View.extend({ }, defaultPresenter : function(){ - var modelJson = this.model ? _.clone(this.model.attributes) : {} + var modelJson = this.model && this.model.attributes ? _.clone(this.model.attributes) : {} + return _.extend(modelJson, { current_user : app.currentUser.attributes, loggedIn : app.currentUser.authenticated() @@ -71,3 +72,66 @@ app.views.Base = Backbone.View.extend({ $(".tooltip").remove(); } }); + +// Mixin to render a collection that fetches more via infinite scroll, for a view that has no template. +// Requires: +// a stream model, bound as this.stream +// a stream's posts, bound as this.collection +// a postClass to be declared +// a #paginate div in the layout +// a call to setupInfiniteScroll + +app.views.infiniteScrollMixin = { + setupInfiniteScroll : function() { + this.postViews = this.postViews || [] + + this.bind("loadMore", this.fetchAndshowLoader, this) + this.stream.bind("fetched", this.hideLoader, this) + this.stream.bind("allPostsLoaded", this.unbindInfScroll, this) + this.collection.bind("add", this.addPost, this); + + var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200); + $(window).scroll(throttledScroll); + }, + + renderTemplate : function() { + if(this.stream.isFetching()) { this.showLoader() } + }, + + addPost : function(post) { + var postView = new this.postClass({ model: post }) + , placeInStream = (this.collection.at(0).id == post.id) ? "prepend" : "append"; + + this.$el[placeInStream](postView.render().el); + this.postViews.push(postView) + }, + + unbindInfScroll : function() { + $(window).unbind("scroll"); + }, + + fetchAndshowLoader : function(){ + if(this.stream.isFetching()) { return false } + this.stream.fetch() + this.showLoader() + }, + + showLoader: function(){ + $("#paginate .loader").removeClass("hidden") + }, + + hideLoader: function() { + $("#paginate .loader").addClass("hidden") + }, + + infScroll : function() { + var $window = $(window) + , distFromTop = $window.height() + $window.scrollTop() + , distFromBottom = $(document).height() - distFromTop + , bufferPx = 500; + + if(distFromBottom < bufferPx) { + this.trigger("loadMore") + } + } +}; diff --git a/app/assets/javascripts/app/views/stream_view.js b/app/assets/javascripts/app/views/stream_view.js index e41b67aaa..945fd3ffa 100644 --- a/app/assets/javascripts/app/views/stream_view.js +++ b/app/assets/javascripts/app/views/stream_view.js @@ -1,74 +1,26 @@ -app.views.Stream = Backbone.View.extend({ +app.views.Stream = Backbone.View.extend(_.extend({ initialize: function(options) { this.stream = this.model this.collection = this.model.posts - - this.setupEvents() - this.setupInfiniteScroll() - this.setupLightbox() this.postViews = [] + + this.setupNSFW() + this.setupLightbox() + this.setupInfiniteScroll() }, - setupEvents : function(){ - this.stream.bind("fetched", this.removeLoader, this) - this.stream.bind("allPostsLoaded", this.unbindInfScroll, this) - this.collection.bind("add", this.addPost, this); - - app.currentUser.bind("nsfwChanged", reRenderPostViews, this) - function reRenderPostViews() { - _.map(this.postViews, function(view){ view.render() }) - } - }, - - addPost : function(post) { - var postView = new app.views.StreamPost({ model: post }) - , placeInStream = (this.collection.at(0).id == post.id) ? "prepend" : "append"; - - this.$el[placeInStream](postView.render().el); - this.postViews.push(postView) - }, - - unbindInfScroll : function() { - $(window).unbind("scroll"); - }, - - render : function() { - if(this.stream.isFetching()) { this.appendLoader() } - return this; - }, - - fetchAndAppendLoader : function(){ - if(this.stream.isFetching()) { return false } - this.stream.fetch() - this.appendLoader() - }, - - appendLoader: function(){ - $("#paginate .loader").removeClass("hidden") - }, - - removeLoader: function() { - $("#paginate .loader").addClass("hidden") - }, + postClass : app.views.StreamPost, setupLightbox : function(){ this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox"); this.$el.delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked); }, - setupInfiniteScroll : function() { - var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200); - $(window).scroll(throttledScroll); - }, + setupNSFW : function(){ + app.currentUser.bind("nsfwChanged", reRenderPostViews, this) - infScroll : function() { - var $window = $(window) - , distFromTop = $window.height() + $window.scrollTop() - , distFromBottom = $(document).height() - distFromTop - , bufferPx = 500; - - if(distFromBottom < bufferPx) { - this.fetchAndAppendLoader() + function reRenderPostViews() { + _.map(this.postViews, function(view){ view.render() }) } } -}); +}, app.views.infiniteScrollMixin)); diff --git a/spec/javascripts/app/views/stream_view_spec.js b/spec/javascripts/app/views/stream_view_spec.js index 6ab6526fb..d1e2ecc74 100644 --- a/spec/javascripts/app/views/stream_view_spec.js +++ b/spec/javascripts/app/views/stream_view_spec.js @@ -43,10 +43,17 @@ describe("app.views.Stream", function() { it("fetches moar when the user is at the bottom of the page", function() { spyOn($.fn, "height").andReturn(0); spyOn($.fn, "scrollTop").andReturn(100); - spyOn(this.view, "fetchAndAppendLoader"); + spyOn(this.view.model, "fetch"); this.view.infScroll(); - expect(this.view.fetchAndAppendLoader).toHaveBeenCalled(); + + waitsFor(function(){ + return this.view.model.fetch.wasCalled + }, "the infinite scroll function didn't fetch the stream") + + runs(function(){ + expect(this.view.model.fetch).toHaveBeenCalled() + }) }); });