FIX: Alter "Take Action" default behaviour to hide post (#24088)

This commit fixes an issue where clicking the default
"Take Action" option on a flag for a post doesn't always
end up with the post hidden.

This is because the "take_action" score bonus doesn’t take into account
the final score required to hide the post.

Especially with the `hide_post_sensitivity` site setting set to `low`
sensitivity, there is a likelihood the score needed to hide the post
won’t be reached.

Now, the default "Take Action" button has been changed to "Hide Post"
to reflect what is actually happening and the description has been
improved, and if "Take Action" is clicked we _always_ hide the post
regardless of score and sensitivity settings. This way the action reflects
expectations of the user.
This commit is contained in:
Martin Brennan 2023-10-30 10:24:35 +10:00 committed by GitHub
parent c0dff8e728
commit 0a4b1b655d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 21 deletions

View File

@ -32,7 +32,7 @@
</:body> </:body>
<:footer> <:footer>
<DButton <DButton
class="btn-primary" class="btn-primary flag-modal__create-flag"
@action={{this.createFlag}} @action={{this.createFlag}}
@disabled={{not this.submitEnabled}} @disabled={{not this.submitEnabled}}
@title="flagging.submit_tooltip" @title="flagging.submit_tooltip"
@ -42,7 +42,7 @@
{{#if this.canSendWarning}} {{#if this.canSendWarning}}
<DButton <DButton
class="btn-danger" class="btn-danger flag-modal__send-warning"
@action={{this.createFlagAsWarning}} @action={{this.createFlagAsWarning}}
@disabled={{not this.submitEnabled}} @disabled={{not this.submitEnabled}}
@icon="exclamation-triangle" @icon="exclamation-triangle"
@ -52,13 +52,14 @@
{{#if this.canTakeAction}} {{#if this.canTakeAction}}
<ReviewableBundledAction <ReviewableBundledAction
class="flag-modal__take-action"
@bundle={{this.flagActions}} @bundle={{this.flagActions}}
@performAction={{this.takeAction}} @performAction={{this.takeAction}}
@reviewableUpdating={{not this.submitEnabled}} @reviewableUpdating={{not this.submitEnabled}}
/> />
<DButton <DButton
class="btn-danger" class="btn-danger flag-modal__flag-for-review"
@action={{this.flagForReview}} @action={{this.flagForReview}}
@disabled={{not this.submitEnabled this.notifyModeratorsFlag}} @disabled={{not this.submitEnabled this.notifyModeratorsFlag}}
@icon="exclamation-triangle" @icon="exclamation-triangle"
@ -68,7 +69,7 @@
{{#if this.showDeleteSpammer}} {{#if this.showDeleteSpammer}}
<DButton <DButton
class="btn-danger delete-spammer" class="btn-danger delete-spammer flag-modal__delete-spammer"
@action={{this.deleteSpammer}} @action={{this.deleteSpammer}}
@disabled={{not this.submitEnabled}} @disabled={{not this.submitEnabled}}
@icon="exclamation-triangle" @icon="exclamation-triangle"

View File

@ -36,7 +36,7 @@ export default class Flag extends Component {
label: I18n.t("flagging.take_action"), label: I18n.t("flagging.take_action"),
actions: [ actions: [
{ {
id: "agree_and_keep", id: "agree_and_hide",
icon: "thumbs-up", icon: "thumbs-up",
label: I18n.t("flagging.take_action_options.default.title"), label: I18n.t("flagging.take_action_options.default.title"),
description: I18n.t("flagging.take_action_options.default.details"), description: I18n.t("flagging.take_action_options.default.details"),

View File

@ -109,8 +109,14 @@ acceptance("flagging", function (needs) {
exists("[data-value='agree_and_silence']"), exists("[data-value='agree_and_silence']"),
"it shows the silence action option" "it shows the silence action option"
); );
await click("[data-value='agree_and_silence']"); assert.ok(
assert.ok(exists(".silence-user-modal"), "it shows the silence modal"); exists("[data-value='agree_and_suspend']"),
"it shows the suspend action option"
);
assert.ok(
exists("[data-value='agree_and_hide']"),
"it shows the hide action option"
);
}); });
test("Can silence from take action", async function (assert) { test("Can silence from take action", async function (assert) {
@ -119,6 +125,7 @@ acceptance("flagging", function (needs) {
await click("#radio_inappropriate"); await click("#radio_inappropriate");
await selectKit(".reviewable-action-dropdown").expand(); await selectKit(".reviewable-action-dropdown").expand();
await click("[data-value='agree_and_silence']"); await click("[data-value='agree_and_silence']");
assert.ok(exists(".silence-user-modal"), "it shows the silence modal");
const silenceUntilCombobox = selectKit(".silence-until .combobox"); const silenceUntilCombobox = selectKit(".silence-until .combobox");
await silenceUntilCombobox.expand(); await silenceUntilCombobox.expand();

View File

@ -3855,8 +3855,8 @@ en:
take_action: "Take Action…" take_action: "Take Action…"
take_action_options: take_action_options:
default: default:
title: "Take Action" title: "Hide Post"
details: "Reach the flag threshold immediately, rather than waiting for more community flags" details: "Reach the flag threshold immediately, hide the post, and agree with all pending flags"
suspend: suspend:
title: "Suspend User" title: "Suspend User"
details: "Reach the flag threshold, and suspend the user" details: "Reach the flag threshold, and suspend the user"
@ -3882,7 +3882,7 @@ en:
ip_address_missing: "(N/A)" ip_address_missing: "(N/A)"
hidden_email_address: "(hidden)" hidden_email_address: "(hidden)"
submit_tooltip: "Submit the private flag" submit_tooltip: "Submit the private flag"
take_action_tooltip: "Reach the flag threshold immediately, rather than waiting for more community flags" take_action_tooltip: "Reach the flag threshold immediately, hide the post, and agree with all pending flags"
cant: "Sorry, you can't flag this post at this time." cant: "Sorry, you can't flag this post at this time."
notify_staff: "Notify staff privately" notify_staff: "Notify staff privately"
formatted_name: formatted_name:

View File

@ -250,7 +250,9 @@ class PostActionCreator
end end
score = ReviewableFlaggedPost.find_by(target: @post)&.score || 0 score = ReviewableFlaggedPost.find_by(target: @post)&.score || 0
@post.hide!(@post_action_type_id) if score >= Reviewable.score_required_to_hide_post if score >= Reviewable.score_required_to_hide_post || @take_action
@post.hide!(@post_action_type_id)
end
end end
# Special case: If you have TL3 and the user is TL0, and the flag is spam, # Special case: If you have TL3 and the user is TL0, and the flag is spam,

View File

@ -271,18 +271,38 @@ RSpec.describe PostActionCreator do
end end
describe "take_action" do describe "take_action" do
before { PostActionCreator.create(Fabricate(:user), post, :inappropriate) } it "will hide the post" do
PostActionCreator
.new(Fabricate(:moderator), post, PostActionType.types[:spam], take_action: true)
.perform
.reviewable
expect(post.reload).to be_hidden
end
it "will agree with the old reviewable" do context "when there is another reviewable on the post" do
reviewable = before { PostActionCreator.create(Fabricate(:user), post, :inappropriate) }
it "will agree with the old reviewable" do
reviewable =
PostActionCreator
.new(Fabricate(:moderator), post, PostActionType.types[:spam], take_action: true)
.perform
.reviewable
expect(reviewable.reload).to be_approved
expect(reviewable.reviewable_scores).to all(be_agreed)
end
end
context "when hide_post_sensitivity is low" do
before { SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low] }
it "still hides the post without considering the score" do
PostActionCreator PostActionCreator
.new(Fabricate(:moderator), post, PostActionType.types[:spam], take_action: true) .new(Fabricate(:moderator), post, PostActionType.types[:spam], take_action: true)
.perform .perform
.reviewable .reviewable
scores = reviewable.reviewable_scores expect(post.reload).to be_hidden
expect(scores[0]).to be_agreed end
expect(scores[1]).to be_agreed
expect(reviewable.reload).to be_approved
end end
end end
@ -307,7 +327,7 @@ RSpec.describe PostActionCreator do
score = result.reviewable.reviewable_scores.last score = result.reviewable.reviewable_scores.last
expect(score.reason).to eq("queued_by_staff") expect(score.reason).to eq("queued_by_staff")
expect(post.reload.hidden?).to eq(true) expect(post.reload).to be_hidden
end end
it "hides the topic even if it has replies" do it "hides the topic even if it has replies" do

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
# frozen_string_literal: true
describe "Flagging post", type: :system, js: true do
fab!(:current_user) { Fabricate(:admin) }
fab!(:first_post) { Fabricate(:post) }
fab!(:post_to_flag) { Fabricate(:post, topic: first_post.topic) }
let(:topic_page) { PageObjects::Pages::Topic.new }
let(:flag_modal) { PageObjects::Modals::Flag.new }
before { sign_in(current_user) }
describe "Using Take Action" do
it "can select the default action to hide the post, agree with other flags, and reach the flag threshold" do
other_flag = Fabricate(:flag, post: post_to_flag, user: Fabricate(:moderator))
expect(other_flag.reload.agreed_at).to be_nil
topic_page.visit_topic(post_to_flag.topic)
topic_page.expand_post_actions(post_to_flag)
topic_page.click_post_action_button(post_to_flag, :flag)
flag_modal.choose_type(:off_topic)
flag_modal.take_action(:agree_and_hide)
expect(
topic_page.post_by_number(post_to_flag).ancestor(".topic-post.post-hidden"),
).to be_present
expect(other_flag.reload.agreed_at).to be_present
end
end
end

View File

@ -6,6 +6,16 @@ module PageObjects
include Capybara::DSL include Capybara::DSL
include RSpec::Matchers include RSpec::Matchers
BODY_SELECTOR = ""
def body
find(".modal-body#{BODY_SELECTOR}")
end
def footer
find(".modal-footer")
end
def close def close
find(".modal-close").click find(".modal-close").click
end end
@ -19,11 +29,11 @@ module PageObjects
end end
def click_primary_button def click_primary_button
find(".modal-footer .btn-primary").click footer.find(".btn-primary").click
end end
def has_content?(content) def has_content?(content)
find(".modal-body").has_content?(content) body.has_content?(content)
end end
def open? def open?

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module PageObjects
module Modals
class Flag < PageObjects::Modals::Base
BODY_SELECTOR = ".flag-modal-body"
MODAL_SELECTOR = ".flag-modal"
def choose_type(type)
body.find("#radio_#{type}").click
end
def confirm_flag
click_primary_button
end
def take_action(action)
select_kit =
PageObjects::Components::SelectKit.new(".modal-footer .reviewable-action-dropdown")
select_kit.expand
select_kit.select_row_by_value(action)
end
end
end
end

View File

@ -84,6 +84,8 @@ module PageObjects
post_by_number(post).find(".bookmark.with-reminder").click post_by_number(post).find(".bookmark.with-reminder").click
when :reply when :reply
post_by_number(post).find(".post-controls .reply").click post_by_number(post).find(".post-controls .reply").click
when :flag
post_by_number(post).find(".post-controls .create-flag").click
end end
end end