Show Gaps in the post stream when filters are active

Conflicts:
	app/assets/javascripts/discourse/templates/topic.js.handlebars
This commit is contained in:
Robin Ward
2013-12-05 16:46:59 -05:00
parent 0fe5ecbb24
commit 79427732b2
14 changed files with 364 additions and 49 deletions
@@ -0,0 +1,48 @@
/**
Handles a gap between posts with a click to load more
@class PostGapComponent
@extends Ember.Component
@namespace Discourse
@module Discourse
**/
Discourse.PostGapComponent = Ember.Component.extend({
classNameBindings: [':gap', 'gap::hidden'],
init: function() {
this._super();
this.set('loading', false);
var before = this.get('before') === 'true',
gaps = before ? this.get('postStream.gaps.before') : this.get('postStream.gaps.after');
if (gaps) {
this.set('gap', gaps[this.get('post.id')]);
}
},
render: function(buffer) {
if (this.get('loading')) {
buffer.push(I18n.t('loading'));
} else {
buffer.push("<i class='icon icon-cut'></i>" + I18n.t('post.gap', {count: this.get('gap.length')}));
}
},
click: function() {
if (this.get('loading') || (!this.get('gap'))) { return false; }
this.set('loading', true);
this.rerender();
var self = this,
postStream = this.get('postStream'),
filler = this.get('before') === 'true' ? postStream.fillGapBefore : postStream.fillGapAfter;
filler.call(postStream, this.get('post'), this.get('gap')).then(function() {
// hide this control after the promise is resolved
self.set('gap', null);
});
return false;
}
});
@@ -500,6 +500,12 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
}
},
/**
Called the the topmost visible post on the page changes.
@method topVisibleChanged
@params {Discourse.Post} post that is at the top
**/
topVisibleChanged: function(post) {
var postStream = this.get('postStream'),
firstLoadedPost = postStream.get('firstLoadedPost');
@@ -523,11 +529,18 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
}
},
bottomVisibleChanged: function(post) {
this.set('progressPosition', post.get('post_number'));
/**
Called the the bottommost visible post on the page changes.
@method bottomVisibleChanged
@params {Discourse.Post} post that is at the bottom
**/
bottomVisibleChanged: function(post) {
var postStream = this.get('postStream'),
lastLoadedPost = postStream.get('lastLoadedPost');
lastLoadedPost = postStream.get('lastLoadedPost'),
index = postStream.get('stream').indexOf(post.get('id'))+1;
this.set('progressPosition', index);
if (lastLoadedPost && lastLoadedPost === post) {
postStream.appendMore();
@@ -126,31 +126,11 @@ Discourse.PostStream = Em.Object.extend({
return result;
}.property('userFilters.[]', 'summary'),
/**
The text describing the current filters. For display in the pop up at the bottom of the
screen.
@property filterDesc
**/
filterDesc: function() {
hasNoFilters: function() {
var streamFilters = this.get('streamFilters');
if (streamFilters.filter && streamFilters.filter === "summary") {
return I18n.t("topic.filters.summary", {
n_summarized_posts: I18n.t("topic.filters.n_summarized_posts", { count: this.get('filteredPostsCount') }),
of_n_posts: I18n.t("topic.filters.of_n_posts", { count: this.get('topic.posts_count') })
});
} else if (streamFilters.username_filters) {
return I18n.t("topic.filters.user", {
n_posts: I18n.t("topic.filters.n_posts", { count: this.get('filteredPostsCount') }),
by_n_users: I18n.t("topic.filters.by_n_users", { count: streamFilters.username_filters.length })
});
}
return "";
return !(streamFilters && ((streamFilters.filter === 'summary') || streamFilters.userFilters));
}.property('streamFilters.[]', 'topic.posts_count', 'posts.length'),
hasNoFilters: Em.computed.empty('filterDesc'),
/**
Returns the window of posts above the current set in the stream, bound to the top of the stream.
This is the collection we'll ask for when scrolling upwards.
@@ -274,6 +254,66 @@ Discourse.PostStream = Em.Object.extend({
},
hasLoadedData: Em.computed.and('hasPosts', 'hasStream'),
/**
Fill in a gap of posts before a particular post
@method fillGapBefore
@paaram {Discourse.Post} post beside gap
@paaram {Array} gap array of post ids to load
@returns {Ember.Deferred} a promise that's resolved when the posts have been added.
**/
fillGapBefore: function(post, gap) {
var postId = post.get('id'),
stream = this.get('stream'),
idx = stream.indexOf(postId),
currentPosts = this.get('posts'),
self = this;
if (idx !== -1) {
// Insert the gap at the appropriate place
stream.splice.apply(stream, [idx, 0].concat(gap));
stream.enumerableContentDidChange();
var postIdx = currentPosts.indexOf(post);
if (postIdx !== -1) {
return this.findPostsByIds(gap).then(function(posts) {
posts.forEach(function(p) {
var stored = self.storePost(p);
if (!currentPosts.contains(stored)) {
currentPosts.insertAt(postIdx++, stored);
}
});
delete self.get('gaps.before')[postId];
});
}
}
return Ember.RSVP.resolve();
},
/**
Fill in a gap of posts after a particular post
@method fillGapAfter
@paaram {Discourse.Post} post beside gap
@paaram {Array} gap array of post ids to load
@returns {Ember.Deferred} a promise that's resolved when the posts have been added.
**/
fillGapAfter: function(post, gap) {
var postId = post.get('id'),
stream = this.get('stream'),
idx = stream.indexOf(postId),
currentPosts = this.get('posts'),
self = this;
if (idx !== -1) {
stream.pushObjects(gap);
return this.appendMore();
}
return Ember.RSVP.resolve();
},
/**
Appends the next window of posts to the stream. Call it when scrolling downwards.
@@ -522,9 +562,9 @@ Discourse.PostStream = Em.Object.extend({
@method updateFromJson
**/
updateFromJson: function(postStreamData) {
var postStream = this;
var postStream = this,
posts = this.get('posts');
var posts = this.get('posts');
posts.clear();
if (postStreamData) {
// Load posts if present
@@ -1,3 +1,5 @@
{{post-gap post=this postStream=controller.postStream before="true"}}
<div class='row'>
{{view Discourse.ReplyHistory contentBinding="replyHistory"}}
</div>
@@ -82,3 +84,5 @@
</div>
</article>
{{post-gap post=this postStream=controller.postStream before="false"}}
@@ -52,7 +52,7 @@
<nav id='topic-progress' title="{{i18n topic.progress.title}}" {{bindAttr class="hideProgress:hidden"}}>
<button id='jump-top' title="{{i18n topic.progress.jump_top}}" {{bindAttr disabled="jumpTopDisabled"}} {{action jumpTop}}><i class="icon-circle-arrow-up"></i></button>
<div class='nums' {{bindAttr title="progressPositionTitle"}}>
<h4>{{progressPosition}}</h4><span {{bindAttr class="hugeNumberOfPosts:hidden"}}> <span>{{i18n of_value}}</span> <h4>{{highest_post_number}}</h4></span>
<h4>{{progressPosition}}</h4><span {{bindAttr class="hugeNumberOfPosts:hidden"}}> <span>{{i18n of_value}}</span> <h4>{{postStream.filteredPostsCount}}</h4></span>
</div>
<button id='jump-bottom' title="{{i18n topic.progress.jump_bottom}}" {{bindAttr disabled="jumpBottomDisabled"}} {{action jumpBottom}}><i class="icon-circle-arrow-down"></i></button>
<div class='bg'>&nbsp;</div>
@@ -121,12 +121,6 @@
{{/if}}
{{/if}}
<div id='topic-filter' {{bindAttr class="postStream.hasNoFilters:hidden"}}>
{{postStream.filterDesc}}
<a href='#' {{action cancelFilter target="postStream"}}>{{i18n topic.filters.cancel}}</a>
</div>
{{render share}}
{{render posterExpansion}}
@@ -1,6 +1,22 @@
@import "common/foundation/variables";
@import "common/foundation/mixins";
.gap {
background-color: #f9f9f9;
border: 1px solid #eee;
padding: 5px 10px;
margin-bottom: 10px;
color: #555;
cursor: pointer;
&:hover {
background-color: #eee;
}
i.icon {
margin-right: 6px;
}
}
.container {
@extend .clearfix;
+11
View File
@@ -0,0 +1,11 @@
class GapSerializer < ApplicationSerializer
attributes :before, :after
def before
@object.before
end
def after
@object.after
end
end
@@ -1,3 +1,6 @@
require_dependency 'gap_serializer'
require_dependency 'post_serializer'
module PostStreamSerializerMixin
def self.included(klass)
@@ -5,17 +8,18 @@ module PostStreamSerializerMixin
end
def post_stream
{ posts: posts,
stream: object.filtered_post_ids }
result = { posts: posts, stream: object.filtered_post_ids }
result[:gaps] = GapSerializer.new(object.gaps, root: false) if object.gaps.present?
result
end
def posts
return @posts if @posts.present?
@posts = []
@highest_number_in_posts = 0
highest_number_in_posts = 0
if object.posts
object.posts.each_with_index do |p, idx|
@highest_number_in_posts = p.post_number if p.post_number > @highest_number_in_posts
highest_number_in_posts = p.post_number if p.post_number > highest_number_in_posts
ps = PostSerializer.new(p, scope: scope, root: false)
ps.topic_slug = object.topic.slug
ps.topic_view = object