mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 00:47:46 -06:00
FEATURE: Silence Close Notifications User Setting (#26072)
This change creates a user setting that they can toggle if they don't want to receive unread notifications when someone closes a topic they have read and are watching/tracking it.
This commit is contained in:
parent
3f1566eeb1
commit
f71e9aad60
@ -167,6 +167,7 @@ export default class extends Controller {
|
|||||||
"tracked_category_ids",
|
"tracked_category_ids",
|
||||||
"watched_first_post_category_ids",
|
"watched_first_post_category_ids",
|
||||||
"watched_precedence_over_muted",
|
"watched_precedence_over_muted",
|
||||||
|
"topics_unread_when_closed",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.siteSettings.tagging_enabled) {
|
if (this.siteSettings.tagging_enabled) {
|
||||||
|
@ -130,6 +130,7 @@ let userOptionFields = [
|
|||||||
"sidebar_link_to_filtered_list",
|
"sidebar_link_to_filtered_list",
|
||||||
"sidebar_show_count_of_new_items",
|
"sidebar_show_count_of_new_items",
|
||||||
"watched_precedence_over_muted",
|
"watched_precedence_over_muted",
|
||||||
|
"topics_unread_when_closed",
|
||||||
];
|
];
|
||||||
|
|
||||||
export function addSaveableUserOptionField(fieldName) {
|
export function addSaveableUserOptionField(fieldName) {
|
||||||
|
@ -49,6 +49,11 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<PreferenceCheckbox
|
||||||
|
@labelKey="user.topics_unread_when_closed"
|
||||||
|
@checked={{this.model.user_option.topics_unread_when_closed}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -7,26 +7,33 @@ class PostTiming < ActiveRecord::Base
|
|||||||
validates_presence_of :post_number
|
validates_presence_of :post_number
|
||||||
validates_presence_of :msecs
|
validates_presence_of :msecs
|
||||||
|
|
||||||
def self.pretend_read(topic_id, actual_read_post_number, pretend_read_post_number)
|
def self.pretend_read(topic_id, actual_read_post_number, pretend_read_post_number, user_ids = nil)
|
||||||
# This is done in SQL cause the logic is quite tricky and we want to do this in one db hit
|
# This is done in SQL cause the logic is quite tricky and we want to do this in one db hit
|
||||||
#
|
#
|
||||||
DB.exec(
|
user_ids_condition = user_ids.present? ? "AND user_id = ANY(ARRAY[:user_ids]::int[])" : ""
|
||||||
"INSERT INTO post_timings(topic_id, user_id, post_number, msecs)
|
sql_query = <<-SQL
|
||||||
|
INSERT INTO post_timings(topic_id, user_id, post_number, msecs)
|
||||||
SELECT :topic_id, user_id, :pretend_read_post_number, 1
|
SELECT :topic_id, user_id, :pretend_read_post_number, 1
|
||||||
FROM post_timings pt
|
FROM post_timings pt
|
||||||
WHERE topic_id = :topic_id AND
|
WHERE topic_id = :topic_id AND
|
||||||
post_number = :actual_read_post_number AND
|
post_number = :actual_read_post_number
|
||||||
NOT EXISTS (
|
#{user_ids_condition}
|
||||||
|
AND NOT EXISTS (
|
||||||
SELECT 1 FROM post_timings pt1
|
SELECT 1 FROM post_timings pt1
|
||||||
WHERE pt1.topic_id = pt.topic_id AND
|
WHERE pt1.topic_id = pt.topic_id AND
|
||||||
pt1.post_number = :pretend_read_post_number AND
|
pt1.post_number = :pretend_read_post_number AND
|
||||||
pt1.user_id = pt.user_id
|
pt1.user_id = pt.user_id
|
||||||
)
|
)
|
||||||
",
|
SQL
|
||||||
|
|
||||||
|
params = {
|
||||||
pretend_read_post_number: pretend_read_post_number,
|
pretend_read_post_number: pretend_read_post_number,
|
||||||
topic_id: topic_id,
|
topic_id: topic_id,
|
||||||
actual_read_post_number: actual_read_post_number,
|
actual_read_post_number: actual_read_post_number,
|
||||||
)
|
}
|
||||||
|
params[:user_ids] = user_ids if user_ids.present?
|
||||||
|
|
||||||
|
DB.exec(sql_query, params)
|
||||||
|
|
||||||
TopicUser.ensure_consistency!(topic_id)
|
TopicUser.ensure_consistency!(topic_id)
|
||||||
end
|
end
|
||||||
|
@ -292,6 +292,7 @@ end
|
|||||||
# sidebar_show_count_of_new_items :boolean default(FALSE), not null
|
# sidebar_show_count_of_new_items :boolean default(FALSE), not null
|
||||||
# watched_precedence_over_muted :boolean
|
# watched_precedence_over_muted :boolean
|
||||||
# chat_separate_sidebar_mode :integer default(0), not null
|
# chat_separate_sidebar_mode :integer default(0), not null
|
||||||
|
# topics_unread_when_closed :boolean default(TRUE), not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -39,7 +39,8 @@ class UserOptionSerializer < ApplicationSerializer
|
|||||||
:seen_popups,
|
:seen_popups,
|
||||||
:sidebar_link_to_filtered_list,
|
:sidebar_link_to_filtered_list,
|
||||||
:sidebar_show_count_of_new_items,
|
:sidebar_show_count_of_new_items,
|
||||||
:watched_precedence_over_muted
|
:watched_precedence_over_muted,
|
||||||
|
:topics_unread_when_closed
|
||||||
|
|
||||||
def auto_track_topics_after_msecs
|
def auto_track_topics_after_msecs
|
||||||
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs
|
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs
|
||||||
|
@ -90,6 +90,21 @@ TopicStatusUpdater =
|
|||||||
# actually read the topic
|
# actually read the topic
|
||||||
PostTiming.pretend_read(topic.id, old_highest_read, topic.highest_post_number)
|
PostTiming.pretend_read(topic.id, old_highest_read, topic.highest_post_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if status.closed? && status.enabled?
|
||||||
|
sql_query = <<-SQL
|
||||||
|
SELECT DISTINCT post_timings.user_id
|
||||||
|
FROM post_timings
|
||||||
|
JOIN user_options ON user_options.user_id = post_timings.user_id
|
||||||
|
WHERE post_timings.topic_id = :topic_id
|
||||||
|
AND user_options.topics_unread_when_closed = 'f'
|
||||||
|
SQL
|
||||||
|
user_ids = DB.query_single(sql_query, topic_id: topic.id)
|
||||||
|
|
||||||
|
if user_ids.present?
|
||||||
|
PostTiming.pretend_read(topic.id, old_highest_read, topic.highest_post_number, user_ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_for(status)
|
def message_for(status)
|
||||||
|
@ -52,6 +52,7 @@ class UserUpdater
|
|||||||
sidebar_link_to_filtered_list
|
sidebar_link_to_filtered_list
|
||||||
sidebar_show_count_of_new_items
|
sidebar_show_count_of_new_items
|
||||||
watched_precedence_over_muted
|
watched_precedence_over_muted
|
||||||
|
topics_unread_when_closed
|
||||||
]
|
]
|
||||||
|
|
||||||
NOTIFICATION_SCHEDULE_ATTRS = -> do
|
NOTIFICATION_SCHEDULE_ATTRS = -> do
|
||||||
|
@ -1774,6 +1774,7 @@ en:
|
|||||||
after_10_minutes: "after 10 minutes"
|
after_10_minutes: "after 10 minutes"
|
||||||
|
|
||||||
notification_level_when_replying: "When I post in a topic, set that topic to"
|
notification_level_when_replying: "When I post in a topic, set that topic to"
|
||||||
|
topics_unread_when_closed: "Consider topics unread when they are closed"
|
||||||
|
|
||||||
invited:
|
invited:
|
||||||
title: "Invites"
|
title: "Invites"
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddTopicsUnreadWhenClosedToUserOptions < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :user_options, :topics_unread_when_closed, :boolean, default: true, null: false
|
||||||
|
end
|
||||||
|
end
|
@ -794,6 +794,9 @@
|
|||||||
},
|
},
|
||||||
"seen_popups": {
|
"seen_popups": {
|
||||||
"type": ["array", "null"]
|
"type": ["array", "null"]
|
||||||
|
},
|
||||||
|
"topics_unread_when_closed": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@ -828,7 +831,8 @@
|
|||||||
"text_size_seq",
|
"text_size_seq",
|
||||||
"title_count_mode",
|
"title_count_mode",
|
||||||
"timezone",
|
"timezone",
|
||||||
"skip_new_user_tips"
|
"skip_new_user_tips",
|
||||||
|
"topics_unread_when_closed"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -214,11 +214,20 @@ module PageObjects
|
|||||||
find(".topic-notifications-button .select-kit-header").click
|
find(".topic-notifications-button .select-kit-header").click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def click_admin_menu_button
|
||||||
|
find("#topic-footer-buttons .topic-admin-menu-button").click
|
||||||
|
end
|
||||||
|
|
||||||
def watch_topic
|
def watch_topic
|
||||||
click_notifications_button
|
click_notifications_button
|
||||||
find('li[data-name="watching"]').click
|
find('li[data-name="watching"]').click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def close_topic
|
||||||
|
click_admin_menu_button
|
||||||
|
find(".topic-admin-popup-menu ul.topic-admin-menu-topic li.topic-admin-close").click
|
||||||
|
end
|
||||||
|
|
||||||
def has_read_post?(post)
|
def has_read_post?(post)
|
||||||
post_by_number(post).has_css?(".read-state.read", visible: :all, wait: 3)
|
post_by_number(post).has_css?(".read-state.read", visible: :all, wait: 3)
|
||||||
end
|
end
|
||||||
|
44
spec/system/topics_unread_when_closed_spec.rb
Normal file
44
spec/system/topics_unread_when_closed_spec.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe "Topics unread when closed", type: :system do
|
||||||
|
fab!(:topics) { Fabricate.times(10, :post).map(&:topic) }
|
||||||
|
let(:topic_list) { PageObjects::Components::TopicList.new }
|
||||||
|
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||||
|
|
||||||
|
context "when closing a topic" do
|
||||||
|
fab!(:admin)
|
||||||
|
fab!(:user)
|
||||||
|
|
||||||
|
it "close notifications do not appear when disabled" do
|
||||||
|
user.user_option.update!(topics_unread_when_closed: false)
|
||||||
|
sign_in(user)
|
||||||
|
topic = topics.third
|
||||||
|
topic_page.visit_topic(topic)
|
||||||
|
topic_page.watch_topic
|
||||||
|
expect(topic_page).to have_read_post(1)
|
||||||
|
|
||||||
|
# Close the topic as an admin
|
||||||
|
TopicStatusUpdater.new(topic, admin).update!("closed", true)
|
||||||
|
|
||||||
|
# Check that the user did not receive a new post notification badge
|
||||||
|
visit("/latest")
|
||||||
|
expect(topic_list).to have_no_unread_badge(topics.third)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "close notifications appear when enabled (the default)" do
|
||||||
|
user.user_option.update!(topics_unread_when_closed: true)
|
||||||
|
sign_in(user)
|
||||||
|
topic = topics.third
|
||||||
|
topic_page.visit_topic(topic)
|
||||||
|
topic_page.watch_topic
|
||||||
|
expect(topic_page).to have_read_post(1)
|
||||||
|
|
||||||
|
# Close the topic as an admin
|
||||||
|
TopicStatusUpdater.new(topic, admin).update!("closed", true)
|
||||||
|
|
||||||
|
# Check that the user did receive a new post notification badge
|
||||||
|
visit("/latest")
|
||||||
|
expect(topic_list).to have_unread_badge(topics.third)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user