mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FIX: dismiss new when topic_user exists without last read (#12103)
The bug was mentioned on meta: https://meta.discourse.org/t/pressing-dismiss-new-doesnt-clear-new-topics/179858 Problem is that sometimes the user has TopicUser records with `last_read_post_number` set as NULL. In that case, the topic is still "new" to them and should be dismissed when they click dismiss button. In addition, I added that condition to post_migration and bumped the number to fix existing records. Migration is written to be idempotent so it will make no harm to already deployed instances.
This commit is contained in:
parent
c4ff6def8e
commit
7829558c6d
@ -17,7 +17,7 @@ class DismissTopics
|
|||||||
@rows ||= @topics_scope
|
@rows ||= @topics_scope
|
||||||
.joins("LEFT JOIN topic_users ON topic_users.topic_id = topics.id AND topic_users.user_id = #{@user.id}")
|
.joins("LEFT JOIN topic_users ON topic_users.topic_id = topics.id AND topic_users.user_id = #{@user.id}")
|
||||||
.where("topics.created_at >= ?", since_date)
|
.where("topics.created_at >= ?", since_date)
|
||||||
.where("topic_users.id IS NULL")
|
.where("topic_users.id IS NULL OR topic_users.last_read_post_number IS NULL")
|
||||||
.where("topics.archetype <> ?", Archetype.private_message)
|
.where("topics.archetype <> ?", Archetype.private_message)
|
||||||
.order("topics.created_at DESC")
|
.order("topics.created_at DESC")
|
||||||
.limit(SiteSetting.max_new_topics).map do |topic|
|
.limit(SiteSetting.max_new_topics).map do |topic|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class MoveNewSinceToNewTable < ActiveRecord::Migration[6.0]
|
|
||||||
def up
|
|
||||||
sql = <<~SQL
|
|
||||||
INSERT INTO dismissed_topic_users (user_id, topic_id, created_at)
|
|
||||||
SELECT users.id, topics.id, user_stats.new_since
|
|
||||||
FROM user_stats
|
|
||||||
JOIN users ON users.id = user_stats.user_id
|
|
||||||
JOIN user_options ON user_options.user_id = users.id
|
|
||||||
LEFT JOIN topics ON topics.created_at <= user_stats.new_since
|
|
||||||
AND topics.archetype <> :private_message
|
|
||||||
AND topics.created_at >= GREATEST(CASE
|
|
||||||
WHEN COALESCE(user_options.new_topic_duration_minutes, :default_duration) = :always THEN users.created_at
|
|
||||||
WHEN COALESCE(user_options.new_topic_duration_minutes, :default_duration) = :last_visit THEN COALESCE(users.previous_visit_at,users.created_at)
|
|
||||||
ELSE (:now::timestamp - INTERVAL '1 MINUTE' * COALESCE(user_options.new_topic_duration_minutes, :default_duration))
|
|
||||||
END, :min_date)
|
|
||||||
AND topics.id IN(SELECT id FROM topics ORDER BY created_at DESC LIMIT :max_new_topics)
|
|
||||||
LEFT JOIN topic_users ON topics.id = topic_users.topic_id AND users.id = topic_users.user_id
|
|
||||||
LEFT JOIN dismissed_topic_users ON dismissed_topic_users.topic_id = topics.id AND users.id = dismissed_topic_users.user_id
|
|
||||||
WHERE user_stats.new_since IS NOT NULL
|
|
||||||
AND topic_users.id IS NULL
|
|
||||||
AND topics.id IS NOT NULL
|
|
||||||
AND dismissed_topic_users.id IS NULL
|
|
||||||
ORDER BY topics.created_at DESC
|
|
||||||
SQL
|
|
||||||
DB.exec(sql,
|
|
||||||
now: DateTime.now,
|
|
||||||
last_visit: User::NewTopicDuration::LAST_VISIT,
|
|
||||||
always: User::NewTopicDuration::ALWAYS,
|
|
||||||
default_duration: SiteSetting.default_other_new_topic_duration_minutes,
|
|
||||||
min_date: Time.at(SiteSetting.min_new_topics_time).to_datetime,
|
|
||||||
private_message: Archetype.private_message,
|
|
||||||
max_new_topics: SiteSetting.max_new_topics)
|
|
||||||
end
|
|
||||||
|
|
||||||
def down
|
|
||||||
raise ActiveRecord::IrreversibleMigration
|
|
||||||
end
|
|
||||||
end
|
|
@ -0,0 +1,52 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class MoveNewSinceToNewTable < ActiveRecord::Migration[6.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
BATCH_SIZE = 30_000
|
||||||
|
|
||||||
|
def up
|
||||||
|
offset = 0
|
||||||
|
loop do
|
||||||
|
user_stat_user_ids = DB.query("SELECT user_id FROM user_stats LIMIT #{BATCH_SIZE} OFFSET :offset", offset: offset).map(&:user_id)
|
||||||
|
break if user_stat_user_ids.count.zero?
|
||||||
|
sql = <<~SQL
|
||||||
|
INSERT INTO dismissed_topic_users (user_id, topic_id, created_at)
|
||||||
|
SELECT users.id, topics.id, user_stats.new_since
|
||||||
|
FROM user_stats
|
||||||
|
JOIN users ON users.id = user_stats.user_id
|
||||||
|
JOIN user_options ON user_options.user_id = users.id
|
||||||
|
LEFT JOIN topics ON topics.created_at <= user_stats.new_since
|
||||||
|
AND topics.archetype <> :private_message
|
||||||
|
AND topics.created_at >= GREATEST(CASE
|
||||||
|
WHEN COALESCE(user_options.new_topic_duration_minutes, :default_duration) = :always THEN users.created_at
|
||||||
|
WHEN COALESCE(user_options.new_topic_duration_minutes, :default_duration) = :last_visit THEN COALESCE(users.previous_visit_at,users.created_at)
|
||||||
|
ELSE (:now::timestamp - INTERVAL '1 MINUTE' * COALESCE(user_options.new_topic_duration_minutes, :default_duration))
|
||||||
|
END, :min_date)
|
||||||
|
AND topics.id IN(SELECT id FROM topics ORDER BY created_at DESC LIMIT :max_new_topics)
|
||||||
|
LEFT JOIN topic_users ON topics.id = topic_users.topic_id AND users.id = topic_users.user_id
|
||||||
|
LEFT JOIN dismissed_topic_users ON dismissed_topic_users.topic_id = topics.id AND users.id = dismissed_topic_users.user_id
|
||||||
|
WHERE user_stats.new_since IS NOT NULL
|
||||||
|
AND user_stats.user_id IN (:user_stat_user_ids)
|
||||||
|
AND topic_users.last_read_post_number IS NULL
|
||||||
|
AND topics.id IS NOT NULL
|
||||||
|
AND dismissed_topic_users.id IS NULL
|
||||||
|
ORDER BY topics.created_at DESC
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
SQL
|
||||||
|
DB.exec(sql,
|
||||||
|
now: DateTime.now,
|
||||||
|
last_visit: User::NewTopicDuration::LAST_VISIT,
|
||||||
|
always: User::NewTopicDuration::ALWAYS,
|
||||||
|
default_duration: SiteSetting.default_other_new_topic_duration_minutes,
|
||||||
|
min_date: Time.at(SiteSetting.min_new_topics_time).to_datetime,
|
||||||
|
private_message: Archetype.private_message,
|
||||||
|
user_stat_user_ids: user_stat_user_ids,
|
||||||
|
max_new_topics: SiteSetting.max_new_topics)
|
||||||
|
offset = offset + BATCH_SIZE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
raise ActiveRecord::IrreversibleMigration
|
||||||
|
end
|
||||||
|
end
|
@ -29,11 +29,17 @@ describe DismissTopics do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'respects seen topics' do
|
it 'respects seen topics' do
|
||||||
Fabricate(:topic_user, user: user, topic: topic1)
|
Fabricate(:topic_user, user: user, topic: topic1, last_read_post_number: 1)
|
||||||
Fabricate(:topic_user, user: user, topic: topic2)
|
Fabricate(:topic_user, user: user, topic: topic2, last_read_post_number: 1)
|
||||||
expect { described_class.new(user, Topic.all).perform! }.to change { DismissedTopicUser.count }.by(0)
|
expect { described_class.new(user, Topic.all).perform! }.to change { DismissedTopicUser.count }.by(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'dismisses when topic user without last_read_post_number' do
|
||||||
|
Fabricate(:topic_user, user: user, topic: topic1, last_read_post_number: nil)
|
||||||
|
Fabricate(:topic_user, user: user, topic: topic2, last_read_post_number: nil)
|
||||||
|
expect { described_class.new(user, Topic.all).perform! }.to change { DismissedTopicUser.count }.by(2)
|
||||||
|
end
|
||||||
|
|
||||||
it 'respects new_topic_duration_minutes' do
|
it 'respects new_topic_duration_minutes' do
|
||||||
user.user_option.update!(new_topic_duration_minutes: 70)
|
user.user_option.update!(new_topic_duration_minutes: 70)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user