From 0464ddcd9b377d8249d8a3c15cfe91d7a8e97bfc Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Wed, 6 Mar 2024 14:08:49 -0700 Subject: [PATCH] FEATURE: Bulk Silent Close Topics (#26043) Using the new bulk select dropdown you can now choose to bulk close topics silently. --- .../components/modal/bulk-topic-actions.gjs | 12 +++-- .../javascripts/discourse/app/models/topic.js | 2 +- lib/topics_bulk_action.rb | 10 ++++ .../page_objects/components/topic_list.rb | 12 +++++ .../components/topic_list_header.rb | 4 ++ spec/system/page_objects/pages/topic.rb | 13 +++++ spec/system/topic_bulk_select_spec.rb | 54 ++++++++++++++++++- 7 files changed, 99 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/modal/bulk-topic-actions.gjs b/app/assets/javascripts/discourse/app/components/modal/bulk-topic-actions.gjs index 28ee13f9148..0af8b8d4399 100644 --- a/app/assets/javascripts/discourse/app/components/modal/bulk-topic-actions.gjs +++ b/app/assets/javascripts/discourse/app/components/modal/bulk-topic-actions.gjs @@ -1,5 +1,6 @@ import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; +import { Input } from "@ember/component"; import { action, computed } from "@ember/object"; import { service } from "@ember/service"; import { Promise } from "rsvp"; @@ -23,6 +24,7 @@ export default class BulkTopicActions extends Component { @tracked categoryId; @tracked loading; @tracked errors; + @tracked isSilent = false; notificationLevelId = null; @@ -69,8 +71,8 @@ export default class BulkTopicActions extends Component { const topicIds = []; const options = {}; - if (this.args.model.allowSilent === true) { - options.silent = true; + if (this.isSilent) { + operation = { type: "silent_close" }; } const tasks = topicChunks.map((topics) => async () => { @@ -300,10 +302,10 @@ export default class BulkTopicActions extends Component { for="topic-bulk-action-options__silent" class="checkbox-label" > - {{i18n "topics.bulk.silent"}} {{/if}} diff --git a/app/assets/javascripts/discourse/app/models/topic.js b/app/assets/javascripts/discourse/app/models/topic.js index 212055f222f..224baf27022 100644 --- a/app/assets/javascripts/discourse/app/models/topic.js +++ b/app/assets/javascripts/discourse/app/models/topic.js @@ -181,7 +181,7 @@ export default class Topic extends RestModel { }; if (options) { - if (options.select) { + if (options.silent) { data.silent = true; } } diff --git a/lib/topics_bulk_action.rb b/lib/topics_bulk_action.rb index bc62ad10e34..3226e71a418 100644 --- a/lib/topics_bulk_action.rb +++ b/lib/topics_bulk_action.rb @@ -13,6 +13,7 @@ class TopicsBulkAction @operations ||= %w[ change_category close + silent_close archive change_notification_level destroy_post_timing @@ -170,6 +171,15 @@ class TopicsBulkAction end end + def silent_close + topics.each do |t| + if guardian.can_moderate?(t) + t.update_status("autoclosed", true, @user) + @changed_ids << t.id + end + end + end + def unlist topics.each do |t| if guardian.can_moderate?(t) diff --git a/spec/system/page_objects/components/topic_list.rb b/spec/system/page_objects/components/topic_list.rb index eda22e2279d..dfb1e8df5cb 100644 --- a/spec/system/page_objects/components/topic_list.rb +++ b/spec/system/page_objects/components/topic_list.rb @@ -38,6 +38,14 @@ module PageObjects page.has_css?("#{topic_list_item_closed(topic)}") end + def has_unread_badge?(topic) + page.has_css?("#{topic_list_item_unread_badge(topic)}") + end + + def has_no_unread_badge?(topic) + page.has_no_css?("#{topic_list_item_unread_badge(topic)}") + end + def click_topic_checkbox(topic) find("#{topic_list_item_class(topic)} input#bulk-select-#{topic.id}").click end @@ -68,6 +76,10 @@ module PageObjects def topic_list_item_closed(topic) "#{topic_list_item_class(topic)} .topic-statuses .topic-status svg.locked" end + + def topic_list_item_unread_badge(topic) + "#{topic_list_item_class(topic)} .topic-post-badges .unread-posts" + end end end end diff --git a/spec/system/page_objects/components/topic_list_header.rb b/spec/system/page_objects/components/topic_list_header.rb index 259dc51fe20..a3bfc07fea0 100644 --- a/spec/system/page_objects/components/topic_list_header.rb +++ b/spec/system/page_objects/components/topic_list_header.rb @@ -46,6 +46,10 @@ module PageObjects find("#bulk-topics-confirm").click end + def click_silent + find("#topic-bulk-action-options__silent").click + end + private def bulk_select_dropdown_item(name) diff --git a/spec/system/page_objects/pages/topic.rb b/spec/system/page_objects/pages/topic.rb index 2514bfae4ac..044e0f69dfa 100644 --- a/spec/system/page_objects/pages/topic.rb +++ b/spec/system/page_objects/pages/topic.rb @@ -210,6 +210,19 @@ module PageObjects @private_message_map_component.is_visible? end + def click_notifications_button + find(".topic-notifications-button .select-kit-header").click + end + + def watch_topic + click_notifications_button + find('li[data-name="watching"]').click + end + + def has_read_post?(post) + post_by_number(post).has_css?(".read-state.read", visible: :all, wait: 3) + end + private def topic_footer_button_id(button) diff --git a/spec/system/topic_bulk_select_spec.rb b/spec/system/topic_bulk_select_spec.rb index fe8ae95f56a..f671489027c 100644 --- a/spec/system/topic_bulk_select_spec.rb +++ b/spec/system/topic_bulk_select_spec.rb @@ -5,13 +5,14 @@ describe "Topic bulk select", type: :system do fab!(:topics) { Fabricate.times(10, :post).map(&:topic) } let(:topic_list_header) { PageObjects::Components::TopicListHeader.new } let(:topic_list) { PageObjects::Components::TopicList.new } + let(:topic_page) { PageObjects::Pages::Topic.new } context "when in topic" do fab!(:admin) - - before { sign_in(admin) } + fab!(:user) it "closes multiple topics" do + sign_in(admin) visit("/latest") expect(page).to have_css(".topic-list button.bulk-select") expect(topic_list_header).to have_bulk_select_button @@ -37,5 +38,54 @@ describe "Topic bulk select", type: :system do topic_list_header.click_bulk_topics_confirm expect(topic_list).to have_closed_status(topics.first) end + + it "closes topics normally" do + # Watch the topic as a user + sign_in(user) + visit("/latest") + topic = topics.third + visit("/t/#{topic.slug}/#{topic.id}") + topic_page.watch_topic + expect(topic_page).to have_read_post(1) + + # Bulk close the topic as an admin + sign_in(admin) + visit("/latest") + topic_list_header.click_bulk_select_button + topic_list.click_topic_checkbox(topics.third) + topic_list_header.click_bulk_select_topics_dropdown + topic_list_header.click_close_topics_button + topic_list_header.click_bulk_topics_confirm + + # Check that the user did receive a new post notification badge + sign_in(user) + visit("/latest") + expect(topic_list).to have_unread_badge(topics.third) + end + + it "closes topics silently" do + # Watch the topic as a user + sign_in(user) + visit("/latest") + topic = topics.first + visit("/t/#{topic.slug}/#{topic.id}") + topic_page.watch_topic + expect(topic_page).to have_read_post(1) + + # Bulk close the topic as an admin + sign_in(admin) + visit("/latest") + topic_list_header.click_bulk_select_button + topic_list.click_topic_checkbox(topics.first) + topic_list_header.click_bulk_select_topics_dropdown + topic_list_header.click_close_topics_button + topic_list_header.click_silent # Check Silent + topic_list_header.click_bulk_topics_confirm + + # Check that the user didn't receive a new post notification badge + sign_in(user) + visit("/latest") + expect(topic_list).to have_no_unread_badge(topics.first) + end end end