mirror of
https://github.com/discourse/discourse.git
synced 2026-08-26 21:27:16 -05:00
Show Gaps in the post stream when filters are active
Conflicts: app/assets/javascripts/discourse/templates/topic.js.handlebars
This commit is contained in:
@@ -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'> </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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user