diff --git a/public/javascripts/app/collections/stream.js b/public/javascripts/app/collections/posts.js similarity index 53% rename from public/javascripts/app/collections/stream.js rename to public/javascripts/app/collections/posts.js index b16e137e125ffef85587065098e653f1ff9ad02a..496d5a7545cdf5371e43c821d382f34f09073322 100644 --- a/public/javascripts/app/collections/stream.js +++ b/public/javascripts/app/collections/posts.js @@ -1,13 +1,5 @@ -app.collections.Stream = Backbone.Collection.extend({ - url: function() { - var path = document.location.pathname; - - if(this.models.length) { - path += "?max_time=" + _.last(this.models).createdAt(); - } - - return path; - }, +app.collections.Posts = Backbone.Collection.extend({ + url : "/posts", model: function(attrs, options) { var modelClass = app.models[attrs.post_type] || app.models.Post diff --git a/public/javascripts/app/models/stream.js b/public/javascripts/app/models/stream.js new file mode 100644 index 0000000000000000000000000000000000000000..8611af3e28d538a4b4e2bc855ba39c45ef34e6a6 --- /dev/null +++ b/public/javascripts/app/models/stream.js @@ -0,0 +1,36 @@ +app.models.Stream = Backbone.Collection.extend({ + initialize : function(){ + this.posts = new app.collections.Posts(); + }, + + url : function(){ + return _.any(this.posts.models) ? this.timeFilteredPath() : this.basePath() + }, + + fetch: function() { + var self = this + + this.posts + .fetch({ + add : true, + url : self.url() + }) + .done( + function(){ + self.trigger("fetched", self); + } + ) + }, + + basePath : function(){ + return document.location.pathname; + }, + + timeFilteredPath : function(){ + return this.basePath() + "?max_time=" + _.last(this.posts.models).createdAt(); + }, + + add : function(models){ + this.posts.add(models) + } +}) diff --git a/public/javascripts/app/router.js b/public/javascripts/app/router.js index ab7234f59fd53240e31cc6f558c2345ab4a2258f..986e2789b3013805c2094b01db36bbc6bfa2f441 100644 --- a/public/javascripts/app/router.js +++ b/public/javascripts/app/router.js @@ -13,10 +13,11 @@ app.Router = Backbone.Router.extend({ }, stream : function() { - app.stream = new app.views.Stream().render(); - $("#main_stream").html(app.stream.el); + app.stream = new app.models.Stream() + app.page = new app.views.Stream().render(); + $("#main_stream").html(app.page.el); - var streamFacesView = new app.views.StreamFaces({collection : app.stream.collection}).render(); + var streamFacesView = new app.views.StreamFaces({collection : app.stream.posts}).render(); $('#selected_aspect_contacts .content').html(streamFacesView.el); } }); diff --git a/public/javascripts/app/views/feedback_view.js b/public/javascripts/app/views/feedback_view.js index 84c1ced12074b507a6dcb32ea81729a89df39b3e..bfa7110adfdcd6b8049cf3ce7673f9ccf23e5890 100644 --- a/public/javascripts/app/views/feedback_view.js +++ b/public/javascripts/app/views/feedback_view.js @@ -20,7 +20,7 @@ app.views.Feedback = app.views.StreamObject.extend({ reshare.save({}, { url: this.model.createReshareUrl, success : function(){ - app.stream.collection.add(reshare); + app.stream.add(reshare); } }); } diff --git a/public/javascripts/app/views/post_view.js b/public/javascripts/app/views/post_view.js index adb46d6b8f1130c44a76b45054f0ebc69a6cfe9b..94011ecfc25bd355f665f79716e7fbd6eb03bb4d 100644 --- a/public/javascripts/app/views/post_view.js +++ b/public/javascripts/app/views/post_view.js @@ -63,9 +63,9 @@ app.views.Post = app.views.StreamObject.extend({ success : function(){ if(!app.stream) { return } - _.each(app.stream.collection.models, function(model){ + _.each(app.stream.posts.models, function(model){ if(model.get("author").id == personId) { - app.stream.collection.remove(model); + app.stream.posts.remove(model); } }) } diff --git a/public/javascripts/app/views/publisher_view.js b/public/javascripts/app/views/publisher_view.js index 989d2ce9af2b36035ebd34c225557520abee8a0c..68e358ca048a6175828764e8fb18d74fd9316b87 100644 --- a/public/javascripts/app/views/publisher_view.js +++ b/public/javascripts/app/views/publisher_view.js @@ -12,7 +12,7 @@ app.views.Publisher = Backbone.View.extend({ }, initialize : function(){ - this.collection = this.collection || new app.collections.Stream; + this.collection = this.collection || new app.collections.Posts; return this; }, @@ -33,7 +33,7 @@ app.views.Publisher = Backbone.View.extend({ }, { url : "/status_messages", success : function() { - app.stream.collection.add(statusMessage.toJSON()); + app.stream.posts.add(statusMessage.toJSON()); } }); diff --git a/public/javascripts/app/views/stream_faces_view.js b/public/javascripts/app/views/stream_faces_view.js index d0979cd8af9742ddb025e0d93ffe086203b00b55..ae5232fc7cef3e9364aa9d2cf8d0eb2f8469f7b7 100644 --- a/public/javascripts/app/views/stream_faces_view.js +++ b/public/javascripts/app/views/stream_faces_view.js @@ -8,7 +8,7 @@ app.views.StreamFaces = app.views.Base.extend({ initialize : function(){ this.updatePeople() - this.collection.bind("add", this.updatePeople, this) + app.stream.posts.bind("add", this.updatePeople, this) }, presenter : function() { diff --git a/public/javascripts/app/views/stream_view.js b/public/javascripts/app/views/stream_view.js index d8c654697337ffa7892dd39c65fe02524a3b1612..8f396f39215464cc32c14248d3eeeb118004a6c1 100644 --- a/public/javascripts/app/views/stream_view.js +++ b/public/javascripts/app/views/stream_view.js @@ -3,46 +3,17 @@ app.views.Stream = Backbone.View.extend({ "click #paginate": "render" }, - initialize: function() { - this.collection = this.collection || new app.collections.Stream; - this.collection.bind("add", this.addPost, this); - + initialize: function(options) { + this.stream = app.stream || new app.models.Stream() + this.collection = this.stream.posts this.publisher = new app.views.Publisher({collection : this.collection}); - // inf scroll - // we're using this._loading to keep track of backbone's collection - // fetching state... is there a better way to do this? - var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200); - $(window).scroll(throttledScroll); - - // lightbox delegation - this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox"); - $(this.el).delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked); - - return this; - }, - - infScroll : function() { - if(this.allContentLoaded || this.isLoading()) { return } - - var $window = $(window); - var distFromTop = $window.height() + $window.scrollTop(); - var distFromBottom = $(document).height() - distFromTop; - var bufferPx = 500; - - if(distFromBottom < bufferPx) { - this.render(); - } - - return this; - }, - - isLoading : function(){ - return this._loading && !this._loading.isResolved(); + this.stream.bind("fetched", this.collectionFetched, this) + this.collection.bind("add", this.addPost, this); + this.setupInfiniteScroll() + this.setupLightbox() }, - allContentLoaded : false, - addPost : function(post) { var postView = new app.views.Post({ model: post }); @@ -55,9 +26,15 @@ app.views.Stream = Backbone.View.extend({ return this; }, - collectionFetched: function(collection, response) { - this.$("#paginate").remove(); + isLoading : function(){ + return this._loading && !this._loading.isResolved(); + }, + + allContentLoaded : false, + + collectionFetched: function(collection, response) { + this.removeLoader() if(!collection.parse(response).length || collection.parse(response).length == 0) { this.allContentLoaded = true; $(window).unbind('scroll') @@ -65,7 +42,7 @@ app.views.Stream = Backbone.View.extend({ } $(this.el).append($("<a>", { - href: this.collection.url(), + href: this.stream.url(), id: "paginate" }).text('Load more posts')); }, @@ -73,13 +50,8 @@ app.views.Stream = Backbone.View.extend({ render : function(evt) { if(evt) { evt.preventDefault(); } - var self = this; - self.addLoader(); - - this._loading = self.collection.fetch({ - add: true, - success: $.proxy(this.collectionFetched, self) - }); + this.addLoader(); + this._loading = this.stream.fetch(); return this; }, @@ -95,5 +67,34 @@ app.views.Stream = Backbone.View.extend({ src : "/images/static-loader.png", "class" : "loader" })); - } + }, + + removeLoader : function(){ + this.$("#paginate").remove(); + }, + + setupLightbox : function(){ + this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox"); + $(this.el).delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked); + }, + + setupInfiniteScroll : function() { + var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200); + $(window).scroll(throttledScroll); + }, + + infScroll : function() { + if(this.allContentLoaded || this.isLoading()) { return } + + var $window = $(window); + var distFromTop = $window.height() + $window.scrollTop(); + var distFromBottom = $(document).height() - distFromTop; + var bufferPx = 500; + + if(distFromBottom < bufferPx) { + this.render(); + } + + return this; + }, }); diff --git a/spec/javascripts/app/collections/stream_spec.js b/spec/javascripts/app/collections/stream_spec.js deleted file mode 100644 index 145c09c57c346f3ca848890388e9b4b65ffd6bf7..0000000000000000000000000000000000000000 --- a/spec/javascripts/app/collections/stream_spec.js +++ /dev/null @@ -1,19 +0,0 @@ -describe("app.collections.Stream", function() { - describe("url", function() { - var stream = new app.collections.Stream(), - expectedPath = document.location.pathname; - - it("returns the correct path", function() { - expect(stream.url()).toEqual(expectedPath); - }); - - it("returns the json path with max_time if the collection has models", function() { - var post = new app.models.Post(); - spyOn(post, "createdAt").andReturn(1234); - - stream.add(post); - - expect(stream.url()).toEqual(expectedPath + "?max_time=1234"); - }); - }); -}); diff --git a/spec/javascripts/app/models/stream_spec.js b/spec/javascripts/app/models/stream_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..064b8d8ddbda502e783e477aa86356931c2b354f --- /dev/null +++ b/spec/javascripts/app/models/stream_spec.js @@ -0,0 +1,39 @@ +describe("app.models.Stream", function() { + beforeEach(function(){ + this.stream = new app.models.Stream(), + this.expectedPath = document.location.pathname; + }) + + describe(".fetch", function() { + var postFetch + beforeEach(function(){ + postFetch = new $.Deferred() + + spyOn(this.stream.posts, "fetch").andCallFake(function(){ + return postFetch + }) + }) + + it("it fetches posts from the window's url, and ads them to tthe collection", function() { + this.stream.fetch() + expect(this.stream.posts.fetch).toHaveBeenCalledWith({ add : true, url : this.expectedPath}); + }); + + it("returns the json path with max_time if the collection has models", function() { + var post = new app.models.Post(); + spyOn(post, "createdAt").andReturn(1234); + this.stream.add(post); + + this.stream.fetch() + expect(this.stream.posts.fetch).toHaveBeenCalledWith({ add : true, url : this.expectedPath + "?max_time=1234"}); + }); + + it("triggers fetched on the stream when it is fetched", function(){ + var fetchedSpy = jasmine.createSpy() + this.stream.bind('fetched', fetchedSpy) + this.stream.fetch() + postFetch.resolve() + expect(fetchedSpy).toHaveBeenCalled() + }) + }); +}); diff --git a/spec/javascripts/app/views/post_view_spec.js b/spec/javascripts/app/views/post_view_spec.js index 790913afd9d1fd35422920651c8bf614d62465a3..aedea512806a0a6a5c54dd40cc6e4a844c8d535b 100644 --- a/spec/javascripts/app/views/post_view_spec.js +++ b/spec/javascripts/app/views/post_view_spec.js @@ -13,7 +13,7 @@ describe("app.views.Post", function(){ var posts = $.parseJSON(spec.readFixture("multi_stream_json"))["posts"]; - this.collection = new app.collections.Stream(posts); + this.collection = new app.collections.Posts(posts); this.statusMessage = this.collection.models[0]; this.reshare = this.collection.models[1]; }) diff --git a/spec/javascripts/app/views/stream_faces_view_spec.js b/spec/javascripts/app/views/stream_faces_view_spec.js index d94a8bc9935292d38e3bb241f8f6707226e30a13..8cdf4eb6cb70d6b0371838e87adc9d194e6daa43 100644 --- a/spec/javascripts/app/views/stream_faces_view_spec.js +++ b/spec/javascripts/app/views/stream_faces_view_spec.js @@ -9,8 +9,11 @@ describe("app.views.StreamFaces", function(){ this.post6 = factory.post({author : rebeccaBlack}) this.post7 = factory.post({author : rebeccaBlack}) - this.stream = new app.collections.Stream([this.post1, this.post2, this.post3, this.post4, this.post5, this.post6, this.post7]); - this.view = new app.views.StreamFaces({collection : this.stream}) + app.stream = new app.models.Stream() + app.stream.add([this.post1, this.post2, this.post3, this.post4, this.post5, this.post6, this.post7]); + this.posts = app.stream.posts + + this.view = new app.views.StreamFaces({collection : this.posts}) }) it("should take them unique", function(){ @@ -19,7 +22,7 @@ describe("app.views.StreamFaces", function(){ }) it("findsPeople when the collection changes", function(){ - this.stream.add(factory.post({author : factory.author({name : "Harriet Tubman"})})) + this.posts.add(factory.post({author : factory.author({name : "Harriet Tubman"})})) expect(this.view.people.length).toBe(6) }) @@ -39,8 +42,8 @@ describe("app.views.StreamFaces", function(){ it("rerenders when people are added, but caps to 15 people", function(){ var posts = _.map(_.range(20), function(){ return factory.post()}) - this.stream.reset(posts) //add 20 posts silently to the collection - this.stream.add(factory.post) //trigger an update + this.posts.reset(posts) //add 20 posts silently to the collection + this.posts.add(factory.post) //trigger an update expect(this.view.$("img").length).toBe(15) }) }) diff --git a/spec/javascripts/app/views/stream_view_spec.js b/spec/javascripts/app/views/stream_view_spec.js index 0ef2cdd8b43bf25a59be51486ed8c47d30dcea22..38160b033cb3a921d4884fc8a625cf20130e0dc5 100644 --- a/spec/javascripts/app/views/stream_view_spec.js +++ b/spec/javascripts/app/views/stream_view_spec.js @@ -4,14 +4,17 @@ describe("app.views.Stream", function(){ this.posts = $.parseJSON(spec.readFixture("multi_stream_json"))["posts"]; - this.collection = new app.collections.Stream(this.posts); + app.stream = new app.models.Stream() + app.stream.add(this.posts); + + this.collection = app.stream.posts this.view = new app.views.Stream({collection : this.collection}); + app.stream.bind("fetched", this.collectionFetched, this) //untested + // do this manually because we've moved loadMore into render?? this.view.render(); - _.each(this.view.collection.models, function(post){ - this.view.addPost(post); - }, this); + _.each(this.view.collection.models, function(post){ this.view.addPost(post); }, this); }) describe("initialize", function(){ @@ -42,7 +45,7 @@ describe("app.views.Stream", function(){ // NOTE: inf scroll happens at 500px beforeEach(function(){ - spyOn(this.view.collection, "fetch") + spyOn(this.view.collection, "fetch").andReturn($.Deferred()) }) context("when the user is at the bottom of the page", function(){