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)