diff --git a/public/javascripts/app/collections/comments.js b/public/javascripts/app/collections/comments.js index 03fc518cb..d564f8682 100644 --- a/public/javascripts/app/collections/comments.js +++ b/public/javascripts/app/collections/comments.js @@ -1,3 +1,7 @@ app.collections.Comments = Backbone.Collection.extend({ - model: app.models.Comment + model: app.models.Comment, + + initialize : function(models, options) { + this.url = "/posts/" + options.post.id + "/comments" //not delegating to post.url() because when it is in a stream collection it delegates to that url + } }); diff --git a/public/javascripts/app/collections/likes.js b/public/javascripts/app/collections/likes.js index d576b8e59..01831d4f9 100644 --- a/public/javascripts/app/collections/likes.js +++ b/public/javascripts/app/collections/likes.js @@ -1,3 +1,7 @@ app.collections.Likes = Backbone.Collection.extend({ - model: app.models.Like + model: app.models.Like, + + initialize : function(models, options) { + this.url = "/posts/" + options.post.id + "/likes" //not delegating to post.url() because when it is in a stream collection it delegates to that url + } }); 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 b16e137e1..496d5a754 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/post.js b/public/javascripts/app/models/post.js index b9641156d..8ab106e49 100644 --- a/public/javascripts/app/models/post.js +++ b/public/javascripts/app/models/post.js @@ -1,21 +1,16 @@ app.models.Post = Backbone.Model.extend({ + urlRoot : "/posts", initialize : function() { - this.comments = new app.collections.Comments(this.get("last_three_comments")); - this.comments.url = this.url() + '/comments'; - - this.likes = new app.collections.Likes(this.get("user_like")); // load in the user like initially - this.likes.url = this.url() + '/likes'; + this.comments = new app.collections.Comments(this.get("last_three_comments"), {post : this}); + this.likes = new app.collections.Likes(this.get("user_like"), { post : this}); // load in the user like initially }, - url : function() { - if(this.id) { - return "/posts/" + this.id; - } else { - return "/posts" - } + createdAt : function() { + return new Date(this.get("created_at")) / 1000; }, - reshareUrl : "/reshares", + createReshareUrl : "/reshares", + reshare : function(){ return this._reshare = this._reshare || new app.models.Reshare({root_guid : this.get("guid")}); }, @@ -33,17 +28,8 @@ app.models.Post = Backbone.Model.extend({ } }, - createdAt : function() { - return new Date(this.get("created_at")) / 1000; - }, - - - likeUrl : function(){ - return this.url() + "/likes" - }, - like : function() { - this.set({ user_like : this.likes.create({}, {url : this.likeUrl()}) }); + this.set({ user_like : this.likes.create() }); }, unlike : function() { diff --git a/public/javascripts/app/router.js b/public/javascripts/app/router.js index ab7234f59..986e2789b 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 68dc013a1..bfa7110ad 100644 --- a/public/javascripts/app/views/feedback_view.js +++ b/public/javascripts/app/views/feedback_view.js @@ -18,9 +18,9 @@ app.views.Feedback = app.views.StreamObject.extend({ if(!window.confirm("Reshare " + this.model.reshareAuthor().name + "'s post?")) { return } var reshare = this.model.reshare() reshare.save({}, { - url: this.model.reshareUrl, + 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 adb46d6b8..94011ecfc 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 989d2ce9a..68e358ca0 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 d0979cd8a..ae5232fc7 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 d8c654697..8f396f392 100644 --- a/public/javascripts/app/views/stream_view.js +++ b/public/javascripts/app/views/stream_view.js @@ -3,25 +3,86 @@ 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); + this.stream.bind("fetched", this.collectionFetched, this) + this.collection.bind("add", this.addPost, this); + this.setupInfiniteScroll() + this.setupLightbox() + }, - // lightbox delegation - this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox"); - $(this.el).delegate("a.stream-photo-link", "click", this.lightbox.lightboxImageClicked); + addPost : function(post) { + var postView = new app.views.Post({ model: post }); + + $(this.el)[ + (this.collection.at(0).id == post.id) + ? "prepend" + : "append" + ](postView.render().el); return this; }, + 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') + return + } + + $(this.el).append($("", { + href: this.stream.url(), + id: "paginate" + }).text('Load more posts')); + }, + + render : function(evt) { + if(evt) { evt.preventDefault(); } + + this.addLoader(); + this._loading = this.stream.fetch(); + + return this; + }, + + addLoader: function(){ + if(this.$("#paginate").length == 0) { + $(this.el).append($("
", { + "id" : "paginate" + })); + } + + this.$("#paginate").html($("", { + 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 } @@ -36,64 +97,4 @@ app.views.Stream = Backbone.View.extend({ return this; }, - - isLoading : function(){ - return this._loading && !this._loading.isResolved(); - }, - - allContentLoaded : false, - - addPost : function(post) { - var postView = new app.views.Post({ model: post }); - - $(this.el)[ - (this.collection.at(0).id == post.id) - ? "prepend" - : "append" - ](postView.render().el); - - return this; - }, - - collectionFetched: function(collection, response) { - this.$("#paginate").remove(); - - if(!collection.parse(response).length || collection.parse(response).length == 0) { - this.allContentLoaded = true; - $(window).unbind('scroll') - return - } - - $(this.el).append($("", { - href: this.collection.url(), - id: "paginate" - }).text('Load more posts')); - }, - - render : function(evt) { - if(evt) { evt.preventDefault(); } - - var self = this; - self.addLoader(); - - this._loading = self.collection.fetch({ - add: true, - success: $.proxy(this.collectionFetched, self) - }); - - return this; - }, - - addLoader: function(){ - if(this.$("#paginate").length == 0) { - $(this.el).append($("
", { - "id" : "paginate" - })); - } - - this.$("#paginate").html($("", { - src : "/images/static-loader.png", - "class" : "loader" - })); - } }); diff --git a/spec/javascripts/app/collections/comments_collection_spec.js b/spec/javascripts/app/collections/comments_collection_spec.js new file mode 100644 index 000000000..7c94d4501 --- /dev/null +++ b/spec/javascripts/app/collections/comments_collection_spec.js @@ -0,0 +1,10 @@ +describe("app.collections.comments", function(){ + describe("url", function(){ + it("should user the post id", function(){ + var post =new app.models.Post({id : 5}) + var collection = new app.collections.Comments([], {post : post}) + expect(collection.url).toBe("/posts/5/comments") + }) + }) +}) + diff --git a/spec/javascripts/app/collections/likes_collections_spec.js b/spec/javascripts/app/collections/likes_collections_spec.js new file mode 100644 index 000000000..292888403 --- /dev/null +++ b/spec/javascripts/app/collections/likes_collections_spec.js @@ -0,0 +1,10 @@ +describe("app.collections.Likes", function(){ + describe("url", function(){ + it("should user the post id", function(){ + var post =new app.models.Post({id : 5}) + var collection = new app.collections.Likes([], {post : post}) + expect(collection.url).toBe("/posts/5/likes") + }) + }) +}) + diff --git a/spec/javascripts/app/collections/stream_spec.js b/spec/javascripts/app/collections/stream_spec.js deleted file mode 100644 index 145c09c57..000000000 --- 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/post_spec.js b/spec/javascripts/app/models/post_spec.js index ed5aede9f..4231096ae 100644 --- a/spec/javascripts/app/models/post_spec.js +++ b/spec/javascripts/app/models/post_spec.js @@ -3,6 +3,15 @@ describe("app.models.Post", function() { this.post = new app.models.Post(); }) + describe("url", function(){ + it("should be /posts when it doesn't have an id", function(){ + expect(new app.models.Post().url()).toBe("/posts") + }) + + it("should be /posts/id when it doesn't have an id", function(){ + expect(new app.models.Post({id: 5}).url()).toBe("/posts/5") + }) + }) describe("createdAt", function() { it("returns the post's created_at as an integer", function() { var date = new Date; diff --git a/spec/javascripts/app/models/stream_spec.js b/spec/javascripts/app/models/stream_spec.js new file mode 100644 index 000000000..064b8d8dd --- /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 790913afd..aedea5128 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 d94a8bc99..8cdf4eb6c 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 0ef2cdd8b..38160b033 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(){