2014-12-20 14:26:46 +08:00
|
|
|
import Ember from 'ember';
|
|
|
|
|
|
|
|
// The post stream is an object which represents the posts in a discussion as
|
|
|
|
// they are displayed on the discussion page, from top to bottom. ...
|
|
|
|
|
|
|
|
export default Ember.ArrayProxy.extend(Ember.Evented, {
|
|
|
|
|
|
|
|
// An array of all of the post IDs, in chronological order, in the discussion.
|
2015-01-16 14:56:10 +08:00
|
|
|
ids: null,
|
|
|
|
|
|
|
|
content: null,
|
|
|
|
|
2014-12-20 14:26:46 +08:00
|
|
|
store: null,
|
|
|
|
discussion: null,
|
|
|
|
|
|
|
|
postLoadCount: 20,
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
this.clear();
|
|
|
|
}.on('init'),
|
|
|
|
|
|
|
|
setup: function(ids) {
|
|
|
|
this.set('ids', ids);
|
2015-01-16 14:56:10 +08:00
|
|
|
this.get('content').objectAt(0).set('indexEnd', this.get('count') - 1);
|
2014-12-20 14:26:46 +08:00
|
|
|
},
|
|
|
|
|
2015-01-16 14:56:10 +08:00
|
|
|
count: Ember.computed.alias('ids.length'),
|
|
|
|
|
|
|
|
loadedCount: function() {
|
|
|
|
return this.get('content').filterBy('content').length;
|
|
|
|
}.property('content.@each'),
|
2014-12-20 14:26:46 +08:00
|
|
|
|
|
|
|
firstLoaded: function() {
|
|
|
|
var first = this.objectAt(0);
|
2015-02-02 14:25:22 +08:00
|
|
|
return first && first.content;
|
2014-12-20 14:26:46 +08:00
|
|
|
}.property('content.@each'),
|
|
|
|
|
|
|
|
lastLoaded: function() {
|
|
|
|
var last = this.objectAt(this.get('length') - 1);
|
2015-02-02 14:25:22 +08:00
|
|
|
return last && last.content;
|
2014-12-20 14:26:46 +08:00
|
|
|
}.property('content.@each'),
|
|
|
|
|
|
|
|
// Clear the contents of the post stream, resetting it to one big gap.
|
|
|
|
clear: function() {
|
2015-01-16 14:56:10 +08:00
|
|
|
var content = Ember.A();
|
2015-02-02 14:25:22 +08:00
|
|
|
content.clear().pushObject(this.makeItem(0, this.get('count') - 1).set('loading', true));
|
2015-01-16 14:56:10 +08:00
|
|
|
this.set('content', content);
|
|
|
|
this.set('ids', Ember.A());
|
2014-12-20 14:26:46 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
loadRange: function(start, end, backwards) {
|
|
|
|
var limit = this.get('postLoadCount');
|
|
|
|
|
|
|
|
// Find the appropriate gap objects in the post stream. When we find
|
|
|
|
// one, we will turn on its loading flag.
|
|
|
|
this.get('content').forEach(function(item) {
|
2015-02-02 14:25:22 +08:00
|
|
|
if (! item.content && ((item.indexStart >= start && item.indexStart <= end) || (item.indexEnd >= start && item.indexEnd <= end))) {
|
2014-12-20 14:26:46 +08:00
|
|
|
item.set('loading', true);
|
|
|
|
item.set('direction', backwards ? 'up' : 'down');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Get a list of post numbers that we'll want to retrieve. If there are
|
|
|
|
// more post IDs than the number of posts we want to load, then take a
|
|
|
|
// slice of the array in the appropriate direction.
|
|
|
|
var ids = this.get('ids').slice(start, end + 1);
|
|
|
|
ids = backwards ? ids.slice(-limit) : ids.slice(0, limit);
|
|
|
|
|
|
|
|
return this.loadPosts(ids);
|
|
|
|
},
|
|
|
|
|
|
|
|
loadPosts: function(ids) {
|
|
|
|
if (! ids.length) {
|
|
|
|
return Ember.RSVP.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
var stream = this;
|
|
|
|
return this.store.find('post', {ids: ids}).then(function(posts) {
|
|
|
|
stream.addPosts(posts);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
loadNearNumber: function(number) {
|
|
|
|
// Find the item in the post stream which is nearest to this number. If
|
|
|
|
// it turns out the be the actual post we're trying to load, then we can
|
|
|
|
// return a resolved promise (i.e. we don't need to make an API
|
|
|
|
// request.) Or, if it's a gap, we'll switch on its loading flag.
|
|
|
|
var item = this.findNearestToNumber(number);
|
|
|
|
if (item) {
|
2015-01-30 09:45:01 +08:00
|
|
|
if (item.get('content.number') == number) {
|
2015-01-16 14:56:10 +08:00
|
|
|
return Ember.RSVP.resolve([item.get('content')]);
|
2015-02-02 14:25:22 +08:00
|
|
|
} else if (! item.content) {
|
2014-12-20 14:26:46 +08:00
|
|
|
item.set('direction', 'down').set('loading', true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var stream = this;
|
|
|
|
return this.store.find('post', {
|
|
|
|
discussions: this.get('discussion.id'),
|
2015-01-16 14:56:10 +08:00
|
|
|
near: number,
|
|
|
|
count: this.get('postLoadCount')
|
2014-12-20 14:26:46 +08:00
|
|
|
}).then(function(posts) {
|
|
|
|
stream.addPosts(posts);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-01-30 10:35:42 +08:00
|
|
|
loadNearIndex: function(index, backwards) {
|
2014-12-20 14:26:46 +08:00
|
|
|
// Find the item in the post stream which is nearest to this index. If
|
|
|
|
// it turns out the be the actual post we're trying to load, then we can
|
|
|
|
// return a resolved promise (i.e. we don't need to make an API
|
|
|
|
// request.) Or, if it's a gap, we'll switch on its loading flag.
|
|
|
|
var item = this.findNearestToIndex(index);
|
|
|
|
if (item) {
|
2015-02-02 14:25:22 +08:00
|
|
|
if (item.content) {
|
2015-01-16 14:56:10 +08:00
|
|
|
return Ember.RSVP.resolve([item.get('content')]);
|
2014-12-20 14:26:46 +08:00
|
|
|
}
|
2015-01-30 10:35:42 +08:00
|
|
|
return this.loadRange(Math.max(item.indexStart, index - this.get('postLoadCount') / 2), item.indexEnd, backwards);
|
2014-12-20 14:26:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ember.RSVP.reject();
|
|
|
|
},
|
|
|
|
|
|
|
|
addPosts: function(posts) {
|
|
|
|
this.trigger('postsLoaded', posts);
|
|
|
|
|
|
|
|
var stream = this;
|
|
|
|
posts.forEach(function(post) {
|
|
|
|
stream.addPost(post);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.trigger('postsAdded');
|
|
|
|
},
|
|
|
|
|
|
|
|
addPost: function(post) {
|
|
|
|
var index = this.get('ids').indexOf(post.get('id'));
|
|
|
|
var content = this.get('content');
|
2015-02-02 14:25:22 +08:00
|
|
|
var makeItem = this.makeItem;
|
2014-12-20 14:26:46 +08:00
|
|
|
|
|
|
|
// Here we loop through each item in the post stream, and find the gap
|
|
|
|
// in which this post should be situated. When we find it, we can replace
|
|
|
|
// it with the post, and new gaps either side if appropriate.
|
|
|
|
content.some(function(item, i) {
|
|
|
|
if (item.indexStart <= index && item.indexEnd >= index) {
|
|
|
|
var newItems = [];
|
|
|
|
if (item.indexStart < index) {
|
2015-02-02 14:25:22 +08:00
|
|
|
newItems.push(makeItem(item.indexStart, index - 1));
|
2014-12-20 14:26:46 +08:00
|
|
|
}
|
2015-02-02 14:25:22 +08:00
|
|
|
newItems.push(makeItem(index, index, post));
|
2014-12-20 14:26:46 +08:00
|
|
|
if (item.indexEnd > index) {
|
2015-02-02 14:25:22 +08:00
|
|
|
newItems.push(makeItem(index + 1, item.indexEnd));
|
2014-12-20 14:26:46 +08:00
|
|
|
}
|
|
|
|
content.replace(i, 1, newItems);
|
|
|
|
return true;
|
|
|
|
}
|
2015-01-16 14:56:10 +08:00
|
|
|
});
|
2014-12-20 14:26:46 +08:00
|
|
|
},
|
|
|
|
|
2015-02-02 14:25:22 +08:00
|
|
|
addPostToEnd: function(post) {
|
|
|
|
var index = this.get('count') - 1;
|
|
|
|
this.get('content').pushObject(this.makeItem(index, index, post));
|
|
|
|
},
|
|
|
|
|
|
|
|
makeItem: function(indexStart, indexEnd, post) {
|
|
|
|
return Ember.Object.create({
|
|
|
|
indexStart: indexStart,
|
|
|
|
indexEnd: indexEnd,
|
|
|
|
content: post
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-01-16 14:56:10 +08:00
|
|
|
findNearestTo: function(index, property) {
|
|
|
|
var nearestItem;
|
2014-12-20 14:26:46 +08:00
|
|
|
this.get('content').some(function(item) {
|
2015-01-16 14:56:10 +08:00
|
|
|
nearestItem = item;
|
|
|
|
if (item.get(property) > index) {
|
2014-12-20 14:26:46 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return nearestItem;
|
2015-01-16 14:56:10 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
findNearestToNumber: function(number) {
|
|
|
|
return this.findNearestTo(number, 'content.number');
|
2014-12-20 14:26:46 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
findNearestToIndex: function(index) {
|
2015-01-16 14:56:10 +08:00
|
|
|
return this.findNearestTo(index, 'indexEnd');
|
2014-12-20 14:26:46 +08:00
|
|
|
}
|
|
|
|
});
|