From 9b1d64bb7611ba34a0c3a6fe6a95c8476fb55993 Mon Sep 17 00:00:00 2001
From: Dennis Collinson <dennis.collective@gmail.com>
Date: Wed, 18 Jan 2012 19:08:25 -0800
Subject: [PATCH] stream model has a posts collection

---
 .../app/collections/{stream.js => posts.js}   | 12 +--
 public/javascripts/app/models/stream.js       | 36 +++++++
 public/javascripts/app/router.js              |  7 +-
 public/javascripts/app/views/feedback_view.js |  2 +-
 public/javascripts/app/views/post_view.js     |  4 +-
 .../javascripts/app/views/publisher_view.js   |  4 +-
 .../app/views/stream_faces_view.js            |  2 +-
 public/javascripts/app/views/stream_view.js   | 95 ++++++++++---------
 .../app/collections/stream_spec.js            | 19 ----
 spec/javascripts/app/models/stream_spec.js    | 39 ++++++++
 spec/javascripts/app/views/post_view_spec.js  |  2 +-
 .../app/views/stream_faces_view_spec.js       | 13 ++-
 .../javascripts/app/views/stream_view_spec.js | 13 ++-
 13 files changed, 152 insertions(+), 96 deletions(-)
 rename public/javascripts/app/collections/{stream.js => posts.js} (53%)
 create mode 100644 public/javascripts/app/models/stream.js
 delete mode 100644 spec/javascripts/app/collections/stream_spec.js
 create mode 100644 spec/javascripts/app/models/stream_spec.js

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 b16e137e12..496d5a7545 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 0000000000..8611af3e28
--- /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 ab7234f59f..986e2789b3 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 84c1ced120..bfa7110adf 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 adb46d6b8f..94011ecfc2 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 989d2ce9af..68e358ca04 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 d0979cd8af..ae5232fc7c 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 d8c6546973..8f396f3921 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 145c09c57c..0000000000
--- 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 0000000000..064b8d8ddb
--- /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 790913afd9..aedea51280 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 d94a8bc993..8cdf4eb6cb 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 0ef2cdd8b4..38160b033c 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(){
-- 
GitLab