diff --git a/app/assets/javascripts/app/app.js b/app/assets/javascripts/app/app.js index 3db760e8f..b792282c9 100644 --- a/app/assets/javascripts/app/app.js +++ b/app/assets/javascripts/app/app.js @@ -1,8 +1,12 @@ //= require_self //= require_tree ./helpers + //= require ./router //= require ./models + //= require ./views +//= require ./views/infinite_stream_view + //= require_tree ./models //= require_tree ./pages //= require_tree ./collections diff --git a/app/assets/javascripts/app/pages/stream.js b/app/assets/javascripts/app/pages/stream.js index 526107abe..29028393b 100644 --- a/app/assets/javascripts/app/pages/stream.js +++ b/app/assets/javascripts/app/pages/stream.js @@ -7,8 +7,6 @@ app.views.NewStream = app.views.InfScroll.extend({ } }); -/*--------------------*/ - app.pages.Stream = app.views.Base.extend({ templateName : "stream", @@ -23,64 +21,48 @@ app.pages.Stream = app.views.Base.extend({ initialize : function(){ this.stream = this.model = new app.models.Stream() - this.stream.preloadOrFetch(); + this.stream.preloadOrFetch() this.streamView = new app.views.NewStream({ model : this.stream }) var interactions = this.interactionsView = new app.views.StreamInteractions() - this.setUpThrottledInteractionScroll(); - this.stream.on("frame:interacted", function(post){ interactions.setInteractions(post) }) + + this.streamView.on('loadMore', this.updateUrlState, this); + this.stream.on("fetched", this.refreshScrollSpy, this) }, postRenderTemplate : function() { this.$("#header").css("background-image", "url(" + app.currentUser.get("wallpaper") + ")") - _.defer(function(){$('body').scrollspy({target : '.stream-frame-wrapper', offset : 50})}) - - - this.setUpHashChangeOnStreamLoad() }, - setUpThrottledInteractionScroll : function(){ - this.focusedPost = undefined; - var self = this; - this.updateInteractions = _.throttle(function(){ - console.log("firing for " + self.focusedPost.get('id')); - self.interactionsView.setInteractions(self.focusedPost) - }, 1000) - }, - - setUpHashChangeOnStreamLoad : function(){ - var self = this; - this.streamView.on('loadMore', function(){ - var post = this.stream.items.last(); - if(post){ - self.navigateToPost(post) - } - self.refreshScrollSpy() - }); + updateUrlState : function(){ + var post = this.stream.items.last(); + if(post){ + this.navigateToPost(post) + } }, navigateToPost : function(post){ app.router.navigate(location.pathname + "?ex=true&max_time=" + post.createdAt(), {replace: true}) }, - triggerInteractionLoad : function(evt){ var id = $(evt.target).data("id"); - console.log("calling triggerInteractiosns for: " + id) this.focusedPost = this.stream.items.get(id) - this.updateInteractions() + + this._throttledInteractions = this._throttledInteractions || _.bind(_.throttle(this.updateInteractions, 1000), this) + this._throttledInteractions() + }, + + updateInteractions: function () { + this.interactionsView.setInteractions(this.focusedPost) }, - //on active guid => this guid - // fire interacted from stream collection w/guid refreshScrollSpy : function(){ - setTimeout(function(){ - $('body').scrollspy('refresh') - }, 2000) + _.defer($('body').scrollspy('refresh')) } }); diff --git a/app/assets/javascripts/app/views.js b/app/assets/javascripts/app/views.js index 52e44d7b0..629a528b9 100644 --- a/app/assets/javascripts/app/views.js +++ b/app/assets/javascripts/app/views.js @@ -95,81 +95,3 @@ app.views.Base = Backbone.View.extend({ } }); -// Mixin to render a collection that fetches more via infinite scroll, for a view that has no template. -// Requires: -// a stream model, assigned to this.stream -// a stream's posts, assigned to this.collection -// a postClass to be declared -// a #paginate div in the layout -// a call to setupInfiniteScroll - -app.views.InfScroll = app.views.Base.extend({ - setupInfiniteScroll : function() { - this.postViews = this.postViews || [] - - this.bind("loadMore", this.fetchAndshowLoader, this) - this.stream.bind("fetched", this.hideLoader, this) - this.stream.bind("allItemsLoaded", this.unbindInfScroll, this) - - this.collection.bind("add", this.addPostView, this); - - var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200); - $(window).scroll(throttledScroll); - }, - - postRenderTemplate : function() { - if(this.stream.isFetching()) { this.showLoader() } - }, - - createPostView : function(post){ - var postView = new this.postClass({ model: post, stream: this.stream }); - this.postViews.push(postView) - return postView - }, - - addPostView : function(post) { - var placeInStream = (this.collection.at(0).id == post.id) ? "prepend" : "append"; - this.$el[placeInStream](this.createPostView(post).render().el); - }, - - unbindInfScroll : function() { - $(window).unbind("scroll"); - }, - - renderTemplate : function(){ - this.renderInitialPosts() - }, - - renderInitialPosts : function(){ - this.$el.empty() - this.stream.items.each(_.bind(function(post){ - this.$el.append(this.createPostView(post).render().el); - }, this)) - }, - - 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/infinite_stream_view.js b/app/assets/javascripts/app/views/infinite_stream_view.js new file mode 100644 index 000000000..8e2ff0b22 --- /dev/null +++ b/app/assets/javascripts/app/views/infinite_stream_view.js @@ -0,0 +1,77 @@ +// Abstract Infinite Scroll View Super Class +// Requires: +// a stream model, assigned to this.stream +// a stream's posts, assigned to this.collection +// a postClass to be declared +// a #paginate div in the layout +// a call to setupInfiniteScroll + +app.views.InfScroll = app.views.Base.extend({ + setupInfiniteScroll : function() { + this.postViews = this.postViews || [] + + this.bind("loadMore", this.fetchAndshowLoader, this) + this.stream.bind("fetched", this.hideLoader, this) + this.stream.bind("allItemsLoaded", this.unbindInfScroll, this) + + this.collection.bind("add", this.addPostView, this); + + var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200); + $(window).scroll(throttledScroll); + }, + + postRenderTemplate : function() { + if(this.stream.isFetching()) { this.showLoader() } + }, + + createPostView : function(post){ + var postView = new this.postClass({ model: post, stream: this.stream }); + this.postViews.push(postView) + return postView + }, + + addPostView : function(post) { + var placeInStream = (this.collection.at(0).id == post.id) ? "prepend" : "append"; + this.$el[placeInStream](this.createPostView(post).render().el); + }, + + unbindInfScroll : function() { + $(window).unbind("scroll"); + }, + + renderTemplate : function(){ + this.renderInitialPosts() + }, + + renderInitialPosts : function(){ + this.$el.empty() + this.stream.items.each(_.bind(function(post){ + this.$el.append(this.createPostView(post).render().el); + }, this)) + }, + + 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/spec/javascripts/app/pages/stream_spec.js b/spec/javascripts/app/pages/stream_spec.js index 4095ff0ab..5e8d9248d 100644 --- a/spec/javascripts/app/pages/stream_spec.js +++ b/spec/javascripts/app/pages/stream_spec.js @@ -6,18 +6,11 @@ describe("app.Pages.Stream", function(){ expect(this.post).toBeTruthy() }) - describe('postRenderTemplate', function(){ it("sets the background-image of #header", function(){ this.page.render() expect(this.page.$('#header').css('background-image')).toBeTruthy() }) - - it('calls setUpHashChangeOnStreamLoad', function(){ - spyOn(this.page, 'setUpHashChangeOnStreamLoad') - this.page.render(); - expect(this.page.setUpHashChangeOnStreamLoad).toHaveBeenCalled() - }) }) describe("rendering", function(){ @@ -34,21 +27,13 @@ describe("app.Pages.Stream", function(){ }) }) - describe("setUpHashChangeOnStreamLoad", function(){ - it('calls navigateToPost on the loadMore event', function(){ - spyOn(this.page, 'navigateToPost') - this.page.setUpHashChangeOnStreamLoad() - this.page.streamView.trigger('loadMore') - expect(this.page.navigateToPost).toHaveBeenCalled() - }) - }) - - describe("navigateToPost", function(){ - it("sets the max time of the url to the created at time of a post", function(){ + context("when more posts are loaded", function(){ + it("navigates to the last post in the stream's max_time", function(){ spyOn(app.router, 'navigate') - this.page.navigateToPost(this.post) var url = location.pathname + "?ex=true&max_time=" + this.post.createdAt() - var options = {replace: true} + , options = {replace: true} + + this.page.streamView.trigger('loadMore') expect(app.router.navigate).toHaveBeenCalledWith(url, options) }) })