FEATURE: Display a toast popup after completing a review action. (#31786)

When performing an action in the review queue, this change makes two improvements:

- The buttons on the reviewable item are disabled, so you can't accidentally multi-click.
- A toast is displayed when the action is complete, as a success indication.
This commit is contained in:
Gary Pendergast
2025-03-18 11:26:59 +11:00
committed by GitHub
parent 4816bceaf6
commit 6cd282eeb3
8 changed files with 68 additions and 3 deletions

View File

@ -1,3 +1,4 @@
import { tracked } from "@glimmer/tracking";
import Component from "@ember/component";
import { action, set } from "@ember/object";
import { getOwner } from "@ember/owner";
@ -49,8 +50,11 @@ export default class ReviewableItem extends Component {
@service siteSettings;
@service currentUser;
@service composer;
@service toasts;
@optionalService adminTools;
@tracked disabled = false;
updating = null;
editing = false;
_updates = null;
@ -201,6 +205,8 @@ export default class ReviewableItem extends Component {
_performConfirmed(performableAction, additionalData = {}) {
let reviewable = this.reviewable;
this.disabled = true;
let performAction = () => {
let version = reviewable.get("version");
this.set("updating", true);
@ -242,6 +248,12 @@ export default class ReviewableItem extends Component {
);
}
if (performableAction.completed_message) {
this.toasts.success({
data: { message: performableAction.completed_message },
});
}
if (this.remove) {
this.remove(performResult.remove_reviewable_ids);
} else {
@ -249,7 +261,10 @@ export default class ReviewableItem extends Component {
}
})
.catch(popupAjaxError)
.finally(() => this.set("updating", false));
.finally(() => {
this.set("updating", false);
this.disabled = false;
});
};
if (performableAction.client_action) {

View File

@ -333,6 +333,7 @@ class ReviewableFlaggedPost < Reviewable
action.description = "#{prefix}.description"
action.client_action = client_action
action.confirm_message = "#{prefix}.confirm" if confirm
action.completed_message = "#{prefix}.complete"
end
end

View File

@ -125,6 +125,7 @@ class ReviewablePost < Reviewable
action.description = "#{prefix}.description"
action.client_action = client_action
action.confirm_message = "#{prefix}.confirm" if confirm
action.completed_message = "#{prefix}.complete"
end
end

View File

@ -39,12 +39,14 @@ class ReviewableQueuedPost < Reviewable
a.icon = "check"
a.label = "reviewables.actions.approve_post.title"
a.confirm_message = "reviewables.actions.approve_post.confirm_closed"
a.completed_message = "reviewables.actions.approve_post.complete"
end
else
if target_created_by.present?
actions.add(:approve_post) do |a|
a.icon = "check"
a.label = "reviewables.actions.approve_post.title"
a.completed_message = "reviewables.actions.approve_post.complete"
end
end
end

View File

@ -9,7 +9,8 @@ class ReviewableActionSerializer < ApplicationSerializer
:description,
:server_action,
:client_action,
:require_reject_reason
:require_reject_reason,
:completed_message
def label
I18n.t(object.label)
@ -27,6 +28,10 @@ class ReviewableActionSerializer < ApplicationSerializer
object.server_action
end
def completed_message
I18n.t(object.completed_message, default: nil)
end
def include_description?
description.present?
end

View File

@ -5614,88 +5614,118 @@ en:
agree_and_keep:
title: "Keep post"
description: "Agree with flag but keep this post unchanged."
complete: "Post kept unchanged."
agree_and_keep_hidden:
title: "Keep post hidden"
description: "Agree with flag and keep the post hidden."
complete: "Post kept hidden."
agree_and_suspend:
title: "Suspend user"
description: "Agree with flag and suspend the user."
complete: "User suspended."
agree_and_silence:
title: "Silence user"
description: "Agree with flag and silence the user."
complete: "User silenced."
agree_and_restore:
title: "Restore post"
description: "Restore the post so that all users can see it."
complete: "Post restored."
agree_and_hide:
title: "Hide post"
description: "Agree with flag and hide this post + automatically send the user a message urging them to edit it."
complete: "Post hidden, user has been notified."
agree_and_edit:
title: "Agree and Edit Post"
description: "Agree with flag and open a composer window to edit the post."
complete: "Flag acknowledged, you can now edit the post."
delete_single:
title: "Delete"
complete: "Post deleted."
delete:
title: "Delete…"
delete_and_ignore:
title: "Ignore flag and delete post"
description: "Ignore the flag by removing it from the queue and delete the post; if the first post, delete the topic as well. "
complete: "Post deleted."
delete_and_ignore_replies:
title: "Ignore flag, delete post and replies"
description: "Ignore the flag by removing it from the queue, delete the post and all of its replies; if the first post, delete the topic as well"
confirm: "Are you sure you want to delete the replies to the post as well?"
complete: "Post and replies deleted."
delete_and_agree:
title: "Delete post"
description: "Agree with flag and delete this post; if the first post, delete the topic as well."
complete: "Post deleted."
delete_and_agree_replies:
title: "Delete post and replies"
description: "Agree with flag and delete this post and all of its replies; if the first post, delete the topic as well."
confirm: "Are you sure you want to delete the replies to the post as well?"
complete: "Post and replies deleted."
disagree_and_restore:
title: "No, restore post"
description: "Restore the post so that all users can see it."
complete: "Post restored."
disagree:
title: "No"
complete: "Flag ignored."
discard_post:
title: "Discard Post"
complete: "Post discarded."
revise_and_reject_post:
title: "Revise Post..."
complete: "Post rejected pending revision."
ignore:
title: "Ignore"
complete: "Flag ignored."
ignore_and_do_nothing:
title: "Do nothing"
description: "Ignore the flag by removing it from the queue without taking any action. Hidden posts will stay hidden and be handled by the auto-tools."
complete: "Flag ignored."
approve:
title: "Approve"
complete: "Post approved."
approve_post:
title: "Approve Post"
confirm_closed: "This topic is closed. Would you like to create the post anyway?"
complete: "Post approved."
reject_post:
title: "Reject Post"
complete: "Post rejected."
approve_user:
title: "Approve User"
complete: "User approved."
reject_user:
title: "Delete User…"
delete:
title: "Delete User"
description: "The user will be deleted from the forum."
complete: "User deleted."
block:
title: "Delete and Block User"
description: "The user will be deleted, and we'll block their IP and email address."
complete: "User deleted and blocked."
reject:
title: "Reject"
bundle_title: "Reject…"
reject_and_suspend:
title: "Reject and Suspend user"
complete: "Post rejected and user suspended."
reject_and_silence:
title: "Reject and Silence user"
complete: "Post rejected and user silenced."
reject_and_delete:
title: "Reject and Delete the post"
complete: "Post rejected and deleted."
reject_and_keep_deleted:
title: "Keep post deleted"
complete: "Post rejected and kept deleted."
approve_and_restore:
title: "Approve and Restore post"
complete: "Post approved and restored."
delete_user:
reason: "Deleted via review queue"
complete: "User deleted."
email_style:
html_missing_placeholder: "The html template must include %{placeholder}"

View File

@ -44,7 +44,8 @@ class Reviewable < ActiveRecord::Base
:confirm_message,
:client_action,
:require_reject_reason,
:custom_modal
:custom_modal,
:completed_message
def initialize(id, icon = nil, button_class = nil, label = nil)
super(id)

View File

@ -8,6 +8,7 @@ describe "Reviewables", type: :system do
fab!(:post)
let(:composer) { PageObjects::Components::Composer.new }
let(:moderator) { Fabricate(:moderator) }
let(:toasts) { PageObjects::Components::Toasts.new }
before { sign_in(admin) }
@ -54,6 +55,7 @@ describe "Reviewables", type: :system do
expect(composer).to be_opened
expect(composer.composer_input.value).to eq(post.raw)
expect(toasts).to have_success(I18n.t("reviewables.actions.agree_and_edit.complete"))
end
it "should open a modal when suspending a user" do
@ -70,6 +72,14 @@ describe "Reviewables", type: :system do
text: I18n.t("js.flagging.take_action_options.suspend.title"),
)
end
it "should show a toast when disagreeing with a flag flag" do
visit("/review")
find(".post-disagree").click
expect(toasts).to have_success(I18n.t("reviewables.actions.disagree.complete"))
end
end
end