Merge branch 'stream-js-cleanup'
Conflicts: public/javascripts/app/models/stream.js
This commit is contained in:
commit
750e26e8fb
8 changed files with 90 additions and 120 deletions
|
|
@ -10,7 +10,9 @@
|
||||||
|
|
||||||
#gs-shim{:title => popover_with_close_html("3. #{t('.stay_updated')}"), 'data-content' => t('.stay_updated_explanation')}
|
#gs-shim{:title => popover_with_close_html("3. #{t('.stay_updated')}"), 'data-content' => t('.stay_updated_explanation')}
|
||||||
|
|
||||||
#main_stream.stream{:data => {:guids => stream.aspect_ids.join(',')}}
|
#main_stream.stream
|
||||||
|
|
||||||
|
#paginate
|
||||||
|
|
||||||
- if current_user.contacts.size < 2
|
- if current_user.contacts.size < 2
|
||||||
= render 'aspects/no_contacts_message'
|
= render 'aspects/no_contacts_message'
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,33 @@ app.models.Stream = Backbone.Collection.extend({
|
||||||
return _.any(this.posts.models) ? this.timeFilteredPath() : this.basePath()
|
return _.any(this.posts.models) ? this.timeFilteredPath() : this.basePath()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_fetching : false,
|
||||||
|
|
||||||
fetch: function() {
|
fetch: function() {
|
||||||
var self = this
|
var self = this
|
||||||
|
|
||||||
|
// we're fetching the collection... there is probably a better way to do this
|
||||||
|
self._fetching = true;
|
||||||
|
|
||||||
this.posts
|
this.posts
|
||||||
.fetch({
|
.fetch({
|
||||||
add : true,
|
add : true,
|
||||||
url : self.url()
|
url : self.url()
|
||||||
})
|
})
|
||||||
.done(
|
.done(
|
||||||
function(response){
|
function(resp){
|
||||||
self.trigger("fetched", self, response);
|
// we're done fetching... there is probably a better way to handle this
|
||||||
|
self._fetching = false;
|
||||||
|
|
||||||
|
self.trigger("fetched", self);
|
||||||
|
|
||||||
|
// all loaded?
|
||||||
|
if(resp.posts && (resp.posts.author || resp.posts.length == 0)) {
|
||||||
|
self.trigger("allPostsLoaded", self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
basePath : function(){
|
basePath : function(){
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,12 @@ app.Router = Backbone.Router.extend({
|
||||||
|
|
||||||
stream : function() {
|
stream : function() {
|
||||||
app.stream = new app.models.Stream()
|
app.stream = new app.models.Stream()
|
||||||
app.page = new app.views.Stream().render();
|
app.page = new app.views.Stream({model : app.stream}).render();
|
||||||
$("#main_stream").html(app.page.el);
|
app.publisher = app.publisher || new app.views.Publisher({collection : app.stream.posts});
|
||||||
|
|
||||||
var streamFacesView = new app.views.StreamFaces({collection : app.stream.posts}).render();
|
var streamFacesView = new app.views.StreamFaces({collection : app.stream.posts}).render();
|
||||||
|
|
||||||
|
$("#main_stream").html(app.page.el);
|
||||||
$('#selected_aspect_contacts .content').html(streamFacesView.el);
|
$('#selected_aspect_contacts .content').html(streamFacesView.el);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ app.views.CommentStream = app.views.Base.extend({
|
||||||
|
|
||||||
initialize: function(options) {
|
initialize: function(options) {
|
||||||
this.model.comments.bind('add', this.appendComment, this);
|
this.model.comments.bind('add', this.appendComment, this);
|
||||||
|
|
||||||
|
// add autoexpanders to new comment textarea
|
||||||
|
this.$('textarea').autoResize();
|
||||||
},
|
},
|
||||||
|
|
||||||
postRenderTemplate : function() {
|
postRenderTemplate : function() {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ app.views.Publisher = Backbone.View.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize : function(){
|
initialize : function(){
|
||||||
this.collection = this.collection || new app.collections.Posts;
|
this.collection = this.collection //takes a Posts collection
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,20 @@ app.views.Stream = Backbone.View.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function(options) {
|
initialize: function(options) {
|
||||||
this.stream = app.stream || new app.models.Stream()
|
this.stream = this.model
|
||||||
this.collection = this.stream.posts
|
this.collection = this.model.posts
|
||||||
this.publisher = new app.views.Publisher({collection : this.collection});
|
|
||||||
|
|
||||||
this.stream.bind("fetched", this.collectionFetched, this)
|
this.setupEvents()
|
||||||
this.collection.bind("add", this.addPost, this);
|
|
||||||
this.setupInfiniteScroll()
|
this.setupInfiniteScroll()
|
||||||
this.setupLightbox()
|
this.setupLightbox()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setupEvents : function(){
|
||||||
|
this.stream.bind("fetched", this.removeLoader, this)
|
||||||
|
this.stream.bind("allPostsLoaded", this.unbindInfScroll, this)
|
||||||
|
this.collection.bind("add", this.addPost, this);
|
||||||
|
},
|
||||||
|
|
||||||
addPost : function(post) {
|
addPost : function(post) {
|
||||||
var postView = new app.views.Post({ model: post });
|
var postView = new app.views.Post({ model: post });
|
||||||
|
|
||||||
|
|
@ -26,51 +30,30 @@ app.views.Stream = Backbone.View.extend({
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoading : function(){
|
unbindInfScroll : function() {
|
||||||
return this._loading && !this._loading.isResolved();
|
$("window").unbind("scroll");
|
||||||
},
|
|
||||||
|
|
||||||
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($("<a>", {
|
|
||||||
href: this.stream.url(),
|
|
||||||
id: "paginate"
|
|
||||||
}).text('Load more posts'));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render : function(evt) {
|
render : function(evt) {
|
||||||
if(evt) { evt.preventDefault(); }
|
if(evt) { evt.preventDefault(); }
|
||||||
|
|
||||||
this.addLoader();
|
// fetch more posts from the stream model
|
||||||
this._loading = this.stream.fetch();
|
if(this.stream.fetch()) {
|
||||||
|
this.appendLoader()
|
||||||
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
addLoader: function(){
|
appendLoader: function(){
|
||||||
if(this.$("#paginate").length == 0) {
|
$("#paginate").html($("<img>", {
|
||||||
$(this.el).append($("<div>", {
|
|
||||||
"id" : "paginate"
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$("#paginate").html($("<img>", {
|
|
||||||
src : "/images/static-loader.png",
|
src : "/images/static-loader.png",
|
||||||
"class" : "loader"
|
"class" : "loader"
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
removeLoader : function(){
|
removeLoader: function() {
|
||||||
this.$("#paginate").remove();
|
$("#paginate").empty();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupLightbox : function(){
|
setupLightbox : function(){
|
||||||
|
|
@ -80,13 +63,11 @@ app.views.Stream = Backbone.View.extend({
|
||||||
|
|
||||||
setupInfiniteScroll : function() {
|
setupInfiniteScroll : function() {
|
||||||
var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200);
|
var throttledScroll = _.throttle($.proxy(this.infScroll, this), 200);
|
||||||
$(window).scroll(throttledScroll);
|
$("window").scroll(throttledScroll);
|
||||||
},
|
},
|
||||||
|
|
||||||
infScroll : function() {
|
infScroll : function() {
|
||||||
if(this.allContentLoaded || this.isLoading()) { return }
|
var $window = $("window");
|
||||||
|
|
||||||
var $window = $(window);
|
|
||||||
var distFromTop = $window.height() + $window.scrollTop();
|
var distFromTop = $window.height() + $window.scrollTop();
|
||||||
var distFromBottom = $(document).height() - distFromTop;
|
var distFromBottom = $(document).height() - distFromTop;
|
||||||
var bufferPx = 500;
|
var bufferPx = 500;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ describe("app.models.Stream", function() {
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
postFetch = new $.Deferred()
|
postFetch = new $.Deferred()
|
||||||
|
|
||||||
spyOn(this.stream.posts, "fetch").andCallFake(function(){
|
spyOn(this.stream.posts, "fetch").andCallFake(function(){
|
||||||
return postFetch
|
return postFetch
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -32,7 +32,23 @@ describe("app.models.Stream", function() {
|
||||||
var fetchedSpy = jasmine.createSpy()
|
var fetchedSpy = jasmine.createSpy()
|
||||||
this.stream.bind('fetched', fetchedSpy)
|
this.stream.bind('fetched', fetchedSpy)
|
||||||
this.stream.fetch()
|
this.stream.fetch()
|
||||||
postFetch.resolve()
|
postFetch.resolve({posts : [1,2,3]})
|
||||||
|
expect(fetchedSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("triggers allPostsLoaded on the stream when zero posts are returned", function(){
|
||||||
|
var fetchedSpy = jasmine.createSpy()
|
||||||
|
this.stream.bind('allPostsLoaded', fetchedSpy)
|
||||||
|
this.stream.fetch()
|
||||||
|
postFetch.resolve({posts : []})
|
||||||
|
expect(fetchedSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("triggers allPostsLoaded on the stream when a Post is returned", function(){
|
||||||
|
var fetchedSpy = jasmine.createSpy()
|
||||||
|
this.stream.bind('allPostsLoaded', fetchedSpy)
|
||||||
|
this.stream.fetch()
|
||||||
|
postFetch.resolve({posts : factory.post().attributes})
|
||||||
expect(fetchedSpy).toHaveBeenCalled()
|
expect(fetchedSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ describe("app.views.Stream", function(){
|
||||||
|
|
||||||
this.posts = $.parseJSON(spec.readFixture("multi_stream_json"))["posts"];
|
this.posts = $.parseJSON(spec.readFixture("multi_stream_json"))["posts"];
|
||||||
|
|
||||||
app.stream = new app.models.Stream()
|
this.stream = new app.models.Stream()
|
||||||
app.stream.add(this.posts);
|
this.stream.add(this.posts);
|
||||||
|
|
||||||
this.collection = app.stream.posts
|
this.view = new app.views.Stream({model : this.stream});
|
||||||
this.view = new app.views.Stream({collection : this.collection});
|
|
||||||
|
|
||||||
app.stream.bind("fetched", this.collectionFetched, this) //untested
|
app.stream.bind("fetched", this.collectionFetched, this) //untested
|
||||||
|
|
||||||
|
|
@ -20,16 +19,15 @@ describe("app.views.Stream", function(){
|
||||||
describe("initialize", function(){
|
describe("initialize", function(){
|
||||||
it("binds an infinite scroll listener", function(){
|
it("binds an infinite scroll listener", function(){
|
||||||
spyOn($.fn, "scroll");
|
spyOn($.fn, "scroll");
|
||||||
|
new app.views.Stream({model : this.stream});
|
||||||
new app.views.Stream();
|
|
||||||
expect($.fn.scroll).toHaveBeenCalled()
|
expect($.fn.scroll).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("#render", function(){
|
describe("#render", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
this.statusMessage = this.collection.models[0];
|
this.statusMessage = this.stream.posts.models[0];
|
||||||
this.reshare = this.collection.models[1];
|
this.reshare = this.stream.posts.models[1];
|
||||||
this.statusElement = $(this.view.$("#" + this.statusMessage.get("guid")));
|
this.statusElement = $(this.view.$("#" + this.statusMessage.get("guid")));
|
||||||
this.reshareElement = $(this.view.$("#" + this.reshare.get("guid")));
|
this.reshareElement = $(this.view.$("#" + this.reshare.get("guid")));
|
||||||
})
|
})
|
||||||
|
|
@ -44,77 +42,31 @@ describe("app.views.Stream", function(){
|
||||||
describe("infScroll", function(){
|
describe("infScroll", function(){
|
||||||
// NOTE: inf scroll happens at 500px
|
// NOTE: inf scroll happens at 500px
|
||||||
|
|
||||||
beforeEach(function(){
|
it("calls render when the user is at the bottom of the page", function(){
|
||||||
spyOn(this.view.collection, "fetch").andReturn($.Deferred())
|
spyOn($.fn, "height").andReturn(0)
|
||||||
})
|
spyOn($.fn, "scrollTop").andReturn(100)
|
||||||
|
spyOn(this.view, "render")
|
||||||
context("when the user is at the bottom of the page", function(){
|
|
||||||
beforeEach(function(){
|
|
||||||
spyOn($.fn, "height").andReturn(0)
|
|
||||||
spyOn($.fn, "scrollTop").andReturn(100)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("calls fetch", function(){
|
|
||||||
spyOn(this.view, "isLoading").andReturn(false)
|
|
||||||
|
|
||||||
this.view.infScroll();
|
|
||||||
expect(this.view.collection.fetch).toHaveBeenCalled();
|
|
||||||
})
|
|
||||||
|
|
||||||
it("does not call fetch if the collection is loading", function(){
|
|
||||||
spyOn(this.view, "isLoading").andReturn(true)
|
|
||||||
|
|
||||||
this.view.infScroll();
|
|
||||||
expect(this.view.collection.fetch).not.toHaveBeenCalled();
|
|
||||||
})
|
|
||||||
|
|
||||||
it("does not call fetch if all content has been fetched", function(){
|
|
||||||
spyOn(this.view, "isLoading").andReturn(false)
|
|
||||||
this.view.allContentLoaded = true;
|
|
||||||
|
|
||||||
this.view.infScroll();
|
|
||||||
expect(this.view.collection.fetch).not.toHaveBeenCalled();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("does not fetch new content when the user is not at the bottom of the page", function(){
|
|
||||||
spyOn(this.view, "isLoading").andReturn(false)
|
|
||||||
|
|
||||||
spyOn($.fn, "height").andReturn(0);
|
|
||||||
spyOn($.fn, "scrollTop").andReturn(-500);
|
|
||||||
|
|
||||||
this.view.infScroll();
|
this.view.infScroll();
|
||||||
expect(this.view.collection.fetch).not.toHaveBeenCalled();
|
expect(this.view.render).toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("collectionFetched", function(){
|
describe("removeLoader", function() {
|
||||||
context("unbinding scroll", function(){
|
it("emptys the pagination div when the stream is fetched", function(){
|
||||||
beforeEach(function(){
|
$("#jasmine_content").append($('<div id="paginate">OMG</div>'))
|
||||||
spyOn($.fn, "unbind")
|
expect($("#paginate").text()).toBe("OMG")
|
||||||
})
|
this.view.stream.trigger("fetched")
|
||||||
|
expect($("#paginate")).toBeEmpty()
|
||||||
it("unbinds scroll if there are no more posts left to load", function(){
|
|
||||||
this.view.collectionFetched(this.collection, {posts : []})
|
|
||||||
expect($.fn.unbind).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("does not fetch new content when the user is fetching one post", function(){
|
|
||||||
this.view.collectionFetched(this.collection, {posts : {}})
|
|
||||||
expect($.fn.unbind).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("sets this.allContentLoaded if there are no more posts left to load", function(){
|
describe("unbindInfScroll", function(){
|
||||||
expect(this.view.allContentLoaded).toBe(false)
|
it("unbinds scroll", function() {
|
||||||
this.view.collectionFetched(this.collection, {posts : []})
|
spyOn($.fn, "unbind")
|
||||||
expect(this.view.allContentLoaded).toBe(true)
|
this.view.unbindInfScroll()
|
||||||
})
|
expect($.fn.unbind.mostRecentCall.object.selector).toBe("window")
|
||||||
|
expect($.fn.unbind).toHaveBeenCalledWith("scroll")
|
||||||
it("does not set this.allContentLoaded if there was a non-empty response from the server", function(){
|
|
||||||
expect(this.view.allContentLoaded).toBe(false)
|
|
||||||
this.view.collectionFetched(this.collection, {posts : this.posts})
|
|
||||||
expect(this.view.allContentLoaded).toBe(false)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue