diff --git a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 index 45716ae2ee0..28a663e3858 100644 --- a/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 +++ b/app/assets/javascripts/discourse/controllers/discovery/topics.js.es6 @@ -85,10 +85,6 @@ const controllerOpts = { return this.get('model.filter') === 'new' && this.get('model.topics.length') > 0; }.property('model.filter', 'model.topics.length'), - tooManyTracked: function(){ - return this.topicTrackingState.tooManyTracked(); - }.property(), - showDismissAtTop: function() { return (this.isFilterPage(this.get('model.filter'), 'new') || this.isFilterPage(this.get('model.filter'), 'unread')) && diff --git a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 index 0005b6e6007..ded0d824c89 100644 --- a/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 +++ b/app/assets/javascripts/discourse/models/topic-tracking-state.js.es6 @@ -236,7 +236,6 @@ const TopicTrackingState = Discourse.Model.extend({ }, countNew(category_id) { - if (this.tooManyTracked()) { return(0); } return _.chain(this.states) .where(isNew) .where(topic => topic.category_id === category_id || !category_id) @@ -244,10 +243,6 @@ const TopicTrackingState = Discourse.Model.extend({ .length; }, - tooManyTracked() { - return this.initialStatesLength >= Discourse.SiteSettings.max_tracked_new_unread; - }, - resetNew() { Object.keys(this.states).forEach(id => { if (this.states[id].last_read_post_number === null) { @@ -257,7 +252,6 @@ const TopicTrackingState = Discourse.Model.extend({ }, countUnread(category_id) { - if (this.tooManyTracked()) { return(0); } return _.chain(this.states) .where(isUnread) .where(topic => topic.category_id === category_id || !category_id) @@ -266,7 +260,6 @@ const TopicTrackingState = Discourse.Model.extend({ }, countCategory(category_id) { - if (this.tooManyTracked()) { return(0); } let sum = 0; _.each(this.states, function(topic){ if (topic.category_id === category_id) { diff --git a/app/assets/javascripts/discourse/templates/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/discovery/topics.hbs index 6afe9d80f82..f3e452a4ab0 100644 --- a/app/assets/javascripts/discourse/templates/discovery/topics.hbs +++ b/app/assets/javascripts/discourse/templates/discovery/topics.hbs @@ -1,7 +1,3 @@ -{{#if tooManyTracked}} -
{{{i18n 'topics.too_many_tracked'}}}
-{{/if}} - {{#if redirectedReason}}
{{redirectedReason}}
{{/if}} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 60b6ba9938b..d9fe0a4976b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -309,7 +309,11 @@ class ApplicationController < ActionController::Base def preload_current_user_data store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, scope: guardian, root: false))) - serializer = ActiveModel::ArraySerializer.new(TopicTrackingState.report(current_user.id), each_serializer: TopicTrackingStateSerializer) + report = TopicTrackingState.report(current_user.id) + if report.length >= SiteSetting.max_tracked_new_unread.to_i + TopicUser.cap_unread_later(current_user.id) + end + serializer = ActiveModel::ArraySerializer.new(report, each_serializer: TopicTrackingStateSerializer) store_preloaded("topicTrackingStates", MultiJson.dump(serializer)) end diff --git a/app/jobs/scheduled/periodical_updates.rb b/app/jobs/scheduled/periodical_updates.rb index 2254235a99d..625d67b8d82 100644 --- a/app/jobs/scheduled/periodical_updates.rb +++ b/app/jobs/scheduled/periodical_updates.rb @@ -32,6 +32,16 @@ module Jobs user_id = hash[:profile].user_id Discourse.handle_job_exception(hash[:ex], error_context(args, "Rebaking user id #{user_id}", user_id: user_id)) end + + TopicUser.cap_unread_backlog! + + offset = (SiteSetting.max_tracked_new_unread * (2/5.0)).to_i + last_new_topic = Topic.order('created_at desc').offset(offset).select(:created_at).first + if last_new_topic + SiteSetting.min_new_topics_time = last_new_topic.created_at.to_i + end + + nil end end diff --git a/app/models/topic_tracking_state.rb b/app/models/topic_tracking_state.rb index b6fae15187a..0da784219c9 100644 --- a/app/models/topic_tracking_state.rb +++ b/app/models/topic_tracking_state.rb @@ -106,11 +106,12 @@ class TopicTrackingState WHEN COALESCE(u.new_topic_duration_minutes, :default_duration) = :always THEN u.created_at WHEN COALESCE(u.new_topic_duration_minutes, :default_duration) = :last_visit THEN COALESCE(u.previous_visit_at,u.created_at) ELSE (:now::timestamp - INTERVAL '1 MINUTE' * COALESCE(u.new_topic_duration_minutes, :default_duration)) - END, us.new_since)", + END, us.new_since, :min_date)", now: DateTime.now, last_visit: User::NewTopicDuration::LAST_VISIT, always: User::NewTopicDuration::ALWAYS, - default_duration: SiteSetting.default_other_new_topic_duration_minutes + default_duration: SiteSetting.default_other_new_topic_duration_minutes, + min_date: Time.at(SiteSetting.min_new_topics_time).to_datetime ).where_values[0] end @@ -125,19 +126,49 @@ class TopicTrackingState # This code needs to be VERY efficient as it is triggered via the message bus and may steal # cycles from usual requests # - - unread = TopicQuery.unread_filter(Topic).where_values.join(" AND ") - new = TopicQuery.new_filter(Topic, "xxx").where_values.join(" AND ").gsub!("'xxx'", treat_as_new_topic_clause) + # + sql = report_raw_sql(topic_id: topic_id) sql = <Dismiss New or Dismiss Posts" bulk: reset_read: "Reset Read" delete: "Delete Topics" diff --git a/config/site_settings.yml b/config/site_settings.yml index 9a8d8ade1ad..78f4b7b1416 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -910,6 +910,12 @@ uncategorized: default: false hidden: true + # Nothing past this threshold is ever considered new + # this is calculated dynamically every 15 minutes + min_new_topics_time: + default: 0 + hidden: true + # Category IDs lounge_category_id: default: -1 diff --git a/spec/models/topic_tracking_state_spec.rb b/spec/models/topic_tracking_state_spec.rb index 2d857e320a9..de8e3ff6b9e 100644 --- a/spec/models/topic_tracking_state_spec.rb +++ b/spec/models/topic_tracking_state_spec.rb @@ -39,6 +39,50 @@ describe TopicTrackingState do expect(report.length).to eq(1) end + + it "correctly handles capping" do + $redis.del TopicUser.unread_cap_key + + user = Fabricate(:user) + + post1 = create_post + Fabricate(:post, topic: post1.topic) + + post2 = create_post + Fabricate(:post, topic: post2.topic) + + post3 = create_post + Fabricate(:post, topic: post3.topic) + + tracking = { + notification_level: TopicUser.notification_levels[:tracking], + last_read_post_number: 1, + highest_seen_post_number: 1 + } + + TopicUser.change(user.id, post1.topic_id, tracking) + TopicUser.change(user.id, post2.topic_id, tracking) + TopicUser.change(user.id, post3.topic_id, tracking) + + report = TopicTrackingState.report(user.id) + expect(report.length).to eq(3) + + SiteSetting.max_tracked_new_unread = 5 + # business logic, we allow for 2/5th new .. 2/5th unread ... 1/5th buffer + + TopicUser.cap_unread_backlog! + + report = TopicTrackingState.report(user.id) + expect(report.length).to eq(3) + + TopicUser.cap_unread_later(user.id) + TopicUser.cap_unread_backlog! + + report = TopicTrackingState.report(user.id) + expect(report.length).to eq(2) + + end + it "correctly gets the tracking state" do report = TopicTrackingState.report(user.id) expect(report.length).to eq(0)