diff --git a/app/assets/javascripts/discourse/components/post-gap.js.es6 b/app/assets/javascripts/discourse/components/post-gap.js.es6 index 4302dc8896c..8cb313f6d19 100644 --- a/app/assets/javascripts/discourse/components/post-gap.js.es6 +++ b/app/assets/javascripts/discourse/components/post-gap.js.es6 @@ -9,9 +9,8 @@ export default Ember.Component.extend({ classNameBindings: [':gap', 'gap::hidden'], - _setup: function() { + initGaps: function(){ this.set('loading', false); - var before = this.get('before') === 'true', gaps = before ? this.get('postStream.gaps.before') : this.get('postStream.gaps.after'); @@ -20,6 +19,11 @@ export default Ember.Component.extend({ } }.on('init'), + gapsChanged: function(){ + this.initGaps(); + this.rerender(); + }.observes('post.hasGap'), + render: function(buffer) { if (this.get('loading')) { buffer.push(I18n.t('loading')); diff --git a/app/assets/javascripts/discourse/models/post_stream.js b/app/assets/javascripts/discourse/models/post_stream.js index b428e97cf5d..864c50174e1 100644 --- a/app/assets/javascripts/discourse/models/post_stream.js +++ b/app/assets/javascripts/discourse/models/post_stream.js @@ -259,6 +259,31 @@ Discourse.PostStream = Em.Object.extend({ }, hasLoadedData: Em.computed.and('hasPosts', 'hasStream'), + collapsePosts: function(from, to){ + var posts = this.get('posts'); + var remove = posts.filter(function(post){ + var postNumber = post.get('post_number'); + return postNumber >= from && postNumber <= to; + }); + + posts.removeObjects(remove); + + // make gap + this.set('gaps', this.get('gaps') || {before: {}, after: {}}); + var before = this.get('gaps.before'); + + var post = posts.find(function(post){ + return post.get('post_number') > to; + }); + + before[post.get('id')] = remove.map(function(post){ + return post.get('id'); + }); + post.set('hasGap', true); + + this.get('stream').enumerableContentDidChange(); + }, + /** Fill in a gap of posts before a particular post @@ -291,6 +316,7 @@ Discourse.PostStream = Em.Object.extend({ delete self.get('gaps.before')[postId]; self.get('stream').enumerableContentDidChange(); + post.set('hasGap', false); }); } } diff --git a/app/assets/javascripts/discourse/views/post_view.js b/app/assets/javascripts/discourse/views/post_view.js index 0c44132d985..087ebe3c9e0 100644 --- a/app/assets/javascripts/discourse/views/post_view.js +++ b/app/assets/javascripts/discourse/views/post_view.js @@ -177,13 +177,30 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, { var replyHistory = post.get('replyHistory'), topicController = this.get('controller'), - origScrollTop = $(window).scrollTop(); + origScrollTop = $(window).scrollTop(), + replyPostNumber = this.get('post.reply_to_post_number'), + postNumber = this.get('post.post_number'), + self = this; if (Discourse.Mobile.mobileView) { - Discourse.URL.routeTo(this.get('post.topic').urlForPostNumber(this.get('post.reply_to_post_number'))); + Discourse.URL.routeTo(this.get('post.topic').urlForPostNumber(replyPostNumber)); return; } + var stream = topicController.get('postStream'); + var offsetFromTop = this.$().position().top - $(window).scrollTop(); + + if(Discourse.SiteSettings.experimental_reply_expansion) { + if(postNumber - replyPostNumber > 1) { + stream.collapsePosts(replyPostNumber + 1, postNumber - 1); + } + + Em.run.next(function() { + Discourse.PostView.highlight(replyPostNumber); + $(window).scrollTop(self.$().position().top - offsetFromTop); + }); + return; + } if (replyHistory.length > 0) { var origHeight = this.$('.embedded-posts.top').height(); @@ -195,7 +212,6 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, { } else { post.set('loadingReplyHistory', true); - var self = this; topicController.get('postStream').findReplyHistory(post).then(function () { post.set('loadingReplyHistory', false); @@ -285,23 +301,27 @@ Discourse.PostView = Discourse.GroupedView.extend(Ember.Evented, { }); Discourse.PostView.reopenClass({ + highlight: function(postNumber){ + var $contents = $('#post_' + postNumber +' .topic-body'), + origColor = $contents.data('orig-color') || $contents.css('backgroundColor'); + + $contents.data("orig-color", origColor); + $contents + .addClass('highlighted') + .stop() + .animate({ backgroundColor: origColor }, 2500, 'swing', function(){ + $contents.removeClass('highlighted'); + $contents.css({'background-color': ''}); + }); + }, + considerHighlighting: function(controller, postNumber) { var highlightNumber = controller.get('highlightOnInsert'); // If we're meant to highlight a post if (highlightNumber === postNumber) { controller.set('highlightOnInsert', null); - var $contents = $('#post_' + postNumber +' .topic-body'), - origColor = $contents.data('orig-color') || $contents.css('backgroundColor'); - - $contents.data("orig-color", origColor); - $contents - .addClass('highlighted') - .stop() - .animate({ backgroundColor: origColor }, 2500, 'swing', function(){ - $contents.removeClass('highlighted'); - $contents.css({'background-color': ''}); - }); + this.highlight(postNumber); } } }); diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 08ada8ca238..f719d89f95e 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -740,6 +740,8 @@ en: suppress_reply_directly_above: "Don't show the expandable in-reply-to on a post when there is only a single reply directly above this post." suppress_reply_when_quoting: "Don't show the expandable in-reply-to on a post when post quotes reply." + experimental_reply_expansion: "Hide intermediate replies when expanding a reply to (experimental)" + topics_per_period_in_top_summary: "Number of top topics shown in the default top topics summary." topics_per_period_in_top_page: "Number of top topics shown on the expanded 'Show More' top topics." redirect_users_to_top_page: "Automatically redirect new and long absent users to the top page." diff --git a/config/site_settings.yml b/config/site_settings.yml index c7c91b839aa..5583b530753 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -351,6 +351,9 @@ posting: default: true suppress_reply_when_quoting: default: true + experimental_reply_expansion: + default: false + client: true post_undo_action_window_mins: 10 max_mentions_per_post: 10 newuser_max_replies_per_topic: 3