From 340525340520bcd78ac5eed2977fc5fbb7b5cbfe Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 3 Jun 2014 11:48:52 +1000 Subject: [PATCH] FEATURE: rush posting read times for newly read posts FEATURE: "read" indicator on posts CHANGE: anon is now assumed to have read everything --- .../discourse/controllers/topic_controller.js | 22 +++++++++------- .../javascripts/discourse/lib/screen_track.js | 26 ++++++++++++++++--- .../discourse/routes/topic_route.js | 2 +- .../discourse/templates/post.js.handlebars | 1 + .../stylesheets/desktop/topic-post.scss | 16 +++++++++++- config/locales/client.en.yml | 1 + lib/topic_view.rb | 1 + spec/components/topic_view_spec.rb | 4 +-- spec/models/site_setting_spec.rb | 2 +- 9 files changed, 57 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/topic_controller.js b/app/assets/javascripts/discourse/controllers/topic_controller.js index 2ed6ccb6b..b629c3772 100644 --- a/app/assets/javascripts/discourse/controllers/topic_controller.js +++ b/app/assets/javascripts/discourse/controllers/topic_controller.js @@ -550,20 +550,22 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected } }.observes('currentPost'), - sawObjects: function(posts) { - if (posts) { - var self = this, - lastReadPostNumber = this.get('last_read_post_number'); + readPosts: function(topicId, postNumbers) { + var postStream = this.get('postStream'); - posts.forEach(function(post) { - var postNumber = post.get('post_number'); - if (postNumber > lastReadPostNumber) { - lastReadPostNumber = postNumber; + if(this.get('postStream.topic.id') === topicId){ + _.each(postStream.get('posts'), function(post){ + // optimise heavy loop + // TODO identity map for postNumber + if(_.include(postNumbers,post.post_number) && !post.read){ + post.set("read", true); } - post.set('read', true); }); - self.set('last_read_post_number', lastReadPostNumber); + var max = _.max(postNumbers); + if(max > this.get('last_read_post_number')){ + this.set('last_read_post_number', max); + } } }, diff --git a/app/assets/javascripts/discourse/lib/screen_track.js b/app/assets/javascripts/discourse/lib/screen_track.js index 91a6111f7..5cadfd764 100644 --- a/app/assets/javascripts/discourse/lib/screen_track.js +++ b/app/assets/javascripts/discourse/lib/screen_track.js @@ -16,7 +16,7 @@ Discourse.ScreenTrack = Ember.Object.extend({ this.reset(); }, - start: function(topicId) { + start: function(topicId, topicController) { var currentTopicId = this.get('topicId'); if (currentTopicId && (currentTopicId !== topicId)) { this.tick(); @@ -34,6 +34,7 @@ Discourse.ScreenTrack = Ember.Object.extend({ } this.set('topicId', topicId); + this.set('topicController', topicController); }, stop: function() { @@ -46,6 +47,7 @@ Discourse.ScreenTrack = Ember.Object.extend({ this.flush(); this.reset(); this.set('topicId', null); + this.set('topicController', null); if (this.get('interval')) { clearInterval(this.get('interval')); this.set('interval', null); @@ -87,7 +89,8 @@ Discourse.ScreenTrack = Ember.Object.extend({ if (!Discourse.User.current()) return; var newTimings = {}, - totalTimings = this.get('totalTimings'); + totalTimings = this.get('totalTimings'), + self = this; _.each(this.get('timings'), function(timing) { if (!totalTimings[timing.postNumber]) @@ -126,6 +129,14 @@ Discourse.ScreenTrack = Ember.Object.extend({ headers: { 'X-SILENCE-LOGGER': 'true' } + }).then(function(){ + var controller = self.get('topicController'); + if(controller){ + var postNumbers = Object.keys(newTimings).map(function(v){ + return parseInt(v,10); + }); + controller.readPosts(topicId, postNumbers); + } }); this.set('topicTime', 0); @@ -145,7 +156,16 @@ Discourse.ScreenTrack = Ember.Object.extend({ var diff = new Date().getTime() - this.get('lastTick'); this.set('lastFlush', this.get('lastFlush') + diff); this.set('lastTick', new Date().getTime()); - if (this.get('lastFlush') > (Discourse.SiteSettings.flush_timings_secs * 1000)) { + + var totalTimings = this.get('totalTimings'), timings = this.get('timings'); + var nextFlush = Discourse.SiteSettings.flush_timings_secs * 1000; + + // rush new post numbers + var rush = _.any(_.filter(timings, function(t){return t.time>0;}), function(t){ + return !totalTimings[t.postNumber]; + }); + + if (this.get('lastFlush') > nextFlush || rush) { this.flush(); } diff --git a/app/assets/javascripts/discourse/routes/topic_route.js b/app/assets/javascripts/discourse/routes/topic_route.js index 4a8336bdd..5820c382f 100644 --- a/app/assets/javascripts/discourse/routes/topic_route.js +++ b/app/assets/javascripts/discourse/routes/topic_route.js @@ -180,7 +180,7 @@ Discourse.TopicRoute = Discourse.Route.extend({ controller.subscribe(); // We reset screen tracking every time a topic is entered - Discourse.ScreenTrack.current().start(model.get('id')); + Discourse.ScreenTrack.current().start(model.get('id'), controller); } }); diff --git a/app/assets/javascripts/discourse/templates/post.js.handlebars b/app/assets/javascripts/discourse/templates/post.js.handlebars index 1a9c0f985..faf30fd5e 100644 --- a/app/assets/javascripts/discourse/templates/post.js.handlebars +++ b/app/assets/javascripts/discourse/templates/post.js.handlebars @@ -57,6 +57,7 @@ {{/if}} {{/if}} +
diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 1c184d379..4f291ab0e 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -51,7 +51,7 @@ h1 .topic-statuses .topic-status i { .reply-to-tab { position: absolute; - right: 350px; + right: 375px; z-index: 400; padding: 13px 6px 5px; border-top: 1px solid scale-color-diff(); @@ -1080,3 +1080,17 @@ span.highlighted { .username.new-user a { color: scale-color($primary, $lightness: 70%); } + +.read-state { + color: scale-color($tertiary, $lightness: 50%); + float: right; + margin-right: 10px; + margin-top: 1px; + font-size: 10px; +} + +.read-state.read { + opacity: 0; + -webkit-transition: opacity 1s ease-out; + transition: opacity 1s ease-out; +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index c35355cf4..8f3ff9cbe 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -940,6 +940,7 @@ en: other: "{{count}} posts hidden" more_links: "{{count}} more..." + unread: "Post is unread" has_replies: one: "Reply" other: "Replies" diff --git a/lib/topic_view.rb b/lib/topic_view.rb index 237c9eafe..7c3b43574 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -183,6 +183,7 @@ class TopicView end def read?(post_number) + return true unless @user read_posts_set.include?(post_number) end diff --git a/spec/components/topic_view_spec.rb b/spec/components/topic_view_spec.rb index 5d536973f..836e61cb6 100644 --- a/spec/components/topic_view_spec.rb +++ b/spec/components/topic_view_spec.rb @@ -169,8 +169,8 @@ describe TopicView do context '.read?' do it 'tracks correctly' do - # anon has nothing - TopicView.new(topic.id).read?(1).should be_false + # anon is assumed to have read everything + TopicView.new(topic.id).read?(1).should be_true # random user has nothing topic_view.read?(1).should be_false diff --git a/spec/models/site_setting_spec.rb b/spec/models/site_setting_spec.rb index 1027a1052..e95ca790b 100644 --- a/spec/models/site_setting_spec.rb +++ b/spec/models/site_setting_spec.rb @@ -88,7 +88,7 @@ describe SiteSetting do end describe "top_menu" do - before(:each) { SiteSetting.stubs(:top_menu).returns('one,-nope|two|three,-not|four,ignored|category/xyz') } + before(:each) { SiteSetting.top_menu = 'one,-nope|two|three,-not|four,ignored|category/xyz' } describe "items" do let(:items) { SiteSetting.top_menu_items }