From a5542eeab07ec43deb3bcd391e2e56ad30ebd676 Mon Sep 17 00:00:00 2001
From: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com>
Date: Mon, 14 Aug 2023 13:02:54 -0500
Subject: [PATCH] DEV: Convert `penalize-user` modal to component-based API
(#22960)
---
.../modal/penalize-user.hbs} | 66 ++++---
.../addon/components/modal/penalize-user.js | 118 ++++++++++++
.../controllers/modals/admin-penalize-user.js | 170 ------------------
.../admin/addon/services/admin-tools.js | 42 ++---
.../discourse/app/services/modal.js | 1 -
.../acceptance/admin-penalize-user-test.js | 2 +-
6 files changed, 167 insertions(+), 232 deletions(-)
rename app/assets/javascripts/admin/addon/{templates/modal/admin-penalize-user.hbs => components/modal/penalize-user.hbs} (62%)
create mode 100644 app/assets/javascripts/admin/addon/components/modal/penalize-user.js
delete mode 100644 app/assets/javascripts/admin/addon/controllers/modals/admin-penalize-user.js
diff --git a/app/assets/javascripts/admin/addon/templates/modal/admin-penalize-user.hbs b/app/assets/javascripts/admin/addon/components/modal/penalize-user.hbs
similarity index 62%
rename from app/assets/javascripts/admin/addon/templates/modal/admin-penalize-user.hbs
rename to app/assets/javascripts/admin/addon/components/modal/penalize-user.hbs
index ea8ae78a8f2..16c774d890b 100644
--- a/app/assets/javascripts/admin/addon/templates/modal/admin-penalize-user.hbs
+++ b/app/assets/javascripts/admin/addon/components/modal/penalize-user.hbs
@@ -1,12 +1,13 @@
-
-
- {{#if this.errorMessage}}
- {{this.errorMessage}}
- {{/if}}
-
+
+ <:body>
{{#if this.canPenalize}}
- {{#if (eq this.penaltyType "suspend")}}
+ {{#if (eq @model.penaltyType "suspend")}}
- {{else if (eq this.penaltyType "silence")}}
+ {{else if (eq @model.penaltyType "silence")}}
{{/if}}
-
- {{#if (eq this.penaltyType "suspend")}}
+ {{#if (eq @model.penaltyType "suspend")}}
{{#if this.siteSettings.hide_suspension_reasons}}
{{html-safe (i18n "admin.user.suspend_reason_hidden_label")}}
@@ -34,48 +34,46 @@
{{/if}}
{{/if}}
-
-
- {{#if this.postId}}
+ {{#if @model.postId}}
{{/if}}
-
{{#if this.user.similar_users}}
{{/if}}
-
{{else}}
- {{#if (eq this.penaltyType "suspend")}}
+ {{#if (eq @model.penaltyType "suspend")}}
{{i18n "admin.user.cant_suspend"}}
- {{else if (eq this.penaltyType "silence")}}
+ {{else if (eq @model.penaltyType "silence")}}
{{i18n "admin.user.cant_silence"}}
{{/if}}
{{/if}}
{{html-safe this.penaltyHistory}}
-
-
-
-
\ No newline at end of file
+
+ <:footer>
+
+
+
+
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/addon/components/modal/penalize-user.js b/app/assets/javascripts/admin/addon/components/modal/penalize-user.js
new file mode 100644
index 00000000000..8e8d1dfd1a4
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/components/modal/penalize-user.js
@@ -0,0 +1,118 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { action } from "@ember/object";
+import { isEmpty } from "@ember/utils";
+import { inject as service } from "@ember/service";
+import { extractError } from "discourse/lib/ajax-error";
+import I18n from "I18n";
+
+export default class PenalizeUser extends Component {
+ @service dialog;
+ @service siteSettings;
+
+ @tracked penalizeUntil = this.args.model.user.next_penalty;
+ @tracked confirmClose = false;
+ @tracked otherUserIds = [];
+ @tracked postAction = "delete";
+ @tracked postEdit = this.args.model.postEdit;
+ @tracked flash;
+ @tracked reason;
+ @tracked message;
+
+ get modalTitle() {
+ if (this.args.model.penaltyType === "suspend") {
+ return "admin.user.suspend_modal_title";
+ } else if (this.args.model.penaltyType === "silence") {
+ return "admin.user.silence_modal_title";
+ }
+ }
+
+ get buttonLabel() {
+ if (this.args.model.penaltyType === "suspend") {
+ return "admin.user.suspend";
+ } else if (this.args.model.penaltyType === "silence") {
+ return "admin.user.silence";
+ }
+ }
+
+ get penaltyHistory() {
+ return I18n.messageFormat("admin.user.penalty_history_MF", {
+ SUSPENDED: this.args.model.user.penalty_counts?.suspended,
+ SILENCED: this.args.model.user.penalty_counts?.silenced,
+ });
+ }
+
+ get canPenalize() {
+ if (this.args.model.penaltyType === "suspend") {
+ return this.args.model.user.canSuspend;
+ } else if (this.args.model.penaltyType === "silence") {
+ return this.args.model.user.canSilence;
+ }
+ return false;
+ }
+
+ get submitDisabled() {
+ return (
+ this.penalizing ||
+ isEmpty(this.penalizeUntil) ||
+ !this.reason ||
+ this.reason.length < 1
+ );
+ }
+
+ @action
+ async penalizeUser() {
+ if (this.submitDisabled) {
+ return;
+ }
+ this.penalizing = true;
+ this.confirmClose = true;
+ if (this.before) {
+ this.before();
+ }
+
+ let result;
+ try {
+ const opts = {
+ reason: this.reason,
+ message: this.message,
+ post_id: this.args.model.postId,
+ post_action: this.postAction,
+ post_edit: this.postEdit,
+ other_user_ids: this.otherUserIds,
+ };
+
+ if (this.args.model.penaltyType === "suspend") {
+ opts.suspend_until = this.penalizeUntil;
+ result = await this.args.model.user.suspend(opts);
+ } else if (this.args.model.penaltyType === "silence") {
+ opts.silenced_till = this.penalizeUntil;
+ result = await this.args.model.user.silence(opts);
+ } else {
+ // eslint-disable-next-line no-console
+ console.error("Unknown penalty type:", this.args.model.penaltyType);
+ }
+ this.args.closeModal();
+ if (this.successCallback) {
+ await this.successCallback(result);
+ }
+ } catch {
+ this.flash = extractError(result);
+ } finally {
+ this.penalizing = false;
+ }
+ }
+
+ @action
+ warnBeforeClosing() {
+ if (!this.confirmClose && (this.reason?.length || this.message?.length)) {
+ this.dialog.confirm({
+ message: I18n.t("admin.user.confirm_cancel_penalty"),
+ didConfirm: () => this.args.closeModal(),
+ });
+ return false;
+ }
+
+ this.args.closeModal();
+ }
+}
diff --git a/app/assets/javascripts/admin/addon/controllers/modals/admin-penalize-user.js b/app/assets/javascripts/admin/addon/controllers/modals/admin-penalize-user.js
deleted file mode 100644
index 2fde82fe8f2..00000000000
--- a/app/assets/javascripts/admin/addon/controllers/modals/admin-penalize-user.js
+++ /dev/null
@@ -1,170 +0,0 @@
-import { inject as service } from "@ember/service";
-import Controller from "@ember/controller";
-import { action } from "@ember/object";
-import { next } from "@ember/runloop";
-import { isEmpty } from "@ember/utils";
-import discourseComputed from "discourse-common/utils/decorators";
-import { extractError } from "discourse/lib/ajax-error";
-import ModalFunctionality from "discourse/mixins/modal-functionality";
-import I18n from "I18n";
-
-export default class AdminPenalizeUserController extends Controller.extend(
- ModalFunctionality
-) {
- @service dialog;
-
- loadingUser = false;
- errorMessage = null;
- penaltyType = null;
- penalizeUntil = null;
- reason = null;
- message = null;
- postId = null;
- postAction = null;
- postEdit = null;
- user = null;
- otherUserIds = null;
- loading = false;
- confirmClose = false;
-
- onShow() {
- this.setProperties({
- loadingUser: true,
- errorMessage: null,
- penaltyType: null,
- penalizeUntil: null,
- reason: null,
- message: null,
- postId: null,
- postAction: "delete",
- postEdit: null,
- user: null,
- otherUserIds: [],
- loading: false,
- errorMessage: null,
- reason: null,
- message: null,
- confirmClose: false,
- });
- }
-
- finishedSetup() {
- this.set("penalizeUntil", this.user?.next_penalty);
- }
-
- beforeClose() {
- if (this.confirmClose) {
- return true;
- }
-
- if (
- (this.reason && this.reason.length > 1) ||
- (this.message && this.message.length > 1)
- ) {
- this.send("hideModal");
- this.dialog.confirm({
- message: I18n.t("admin.user.confirm_cancel_penalty"),
- didConfirm: () => {
- next(() => {
- this.set("confirmClose", true);
- this.send("closeModal");
- });
- },
- didCancel: () => this.send("reopenModal"),
- });
-
- return false;
- }
- }
-
- @discourseComputed("penaltyType")
- modalTitle(penaltyType) {
- if (penaltyType === "suspend") {
- return "admin.user.suspend_modal_title";
- } else if (penaltyType === "silence") {
- return "admin.user.silence_modal_title";
- }
- }
-
- @discourseComputed("penaltyType")
- buttonLabel(penaltyType) {
- if (penaltyType === "suspend") {
- return "admin.user.suspend";
- } else if (penaltyType === "silence") {
- return "admin.user.silence";
- }
- }
-
- @discourseComputed(
- "user.penalty_counts.suspended",
- "user.penalty_counts.silenced"
- )
- penaltyHistory(suspendedCount, silencedCount) {
- return I18n.messageFormat("admin.user.penalty_history_MF", {
- SUSPENDED: suspendedCount,
- SILENCED: silencedCount,
- });
- }
-
- @discourseComputed("penaltyType", "user.canSuspend", "user.canSilence")
- canPenalize(penaltyType, canSuspend, canSilence) {
- if (penaltyType === "suspend") {
- return canSuspend;
- } else if (penaltyType === "silence") {
- return canSilence;
- }
-
- return false;
- }
-
- @discourseComputed("penalizing", "penalizeUntil", "reason")
- submitDisabled(penalizing, penalizeUntil, reason) {
- return penalizing || isEmpty(penalizeUntil) || !reason || reason.length < 1;
- }
-
- @action
- async penalizeUser() {
- if (this.submitDisabled) {
- return;
- }
-
- this.set("penalizing", true);
- this.set("confirmClose", true);
-
- if (this.before) {
- this.before();
- }
-
- let result;
- try {
- const opts = {
- reason: this.reason,
- message: this.message,
- post_id: this.postId,
- post_action: this.postAction,
- post_edit: this.postEdit,
- other_user_ids: this.otherUserIds,
- };
-
- if (this.penaltyType === "suspend") {
- opts.suspend_until = this.penalizeUntil;
- result = await this.user.suspend(opts);
- } else if (this.penaltyType === "silence") {
- opts.silenced_till = this.penalizeUntil;
- result = await this.user.silence(opts);
- } else {
- // eslint-disable-next-line no-console
- console.error("Unknown penalty type:", this.penaltyType);
- }
-
- this.send("closeModal");
- if (this.successCallback) {
- await this.successCallback(result);
- }
- } catch {
- this.set("errorMessage", extractError(result));
- } finally {
- this.set("penalizing", false);
- }
- }
-}
diff --git a/app/assets/javascripts/admin/addon/services/admin-tools.js b/app/assets/javascripts/admin/addon/services/admin-tools.js
index 79944fc1475..5c14daa805a 100644
--- a/app/assets/javascripts/admin/addon/services/admin-tools.js
+++ b/app/assets/javascripts/admin/addon/services/admin-tools.js
@@ -4,14 +4,16 @@ import { Promise } from "rsvp";
import Service, { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { getOwner } from "discourse-common/lib/get-owner";
-import showModal from "discourse/lib/show-modal";
import { htmlSafe } from "@ember/template";
+import { action } from "@ember/object";
+import PenalizeUserModal from "admin/components/modal/penalize-user";
// A service that can act as a bridge between the front end Discourse application
// and the admin application. Use this if you need front end code to access admin
// modules. Inject it optionally, and if it exists go to town!
export default class AdminToolsService extends Service {
@service dialog;
+ @service modal;
showActionLogs(target, filters) {
const controller = getOwner(target).lookup(
@@ -39,42 +41,30 @@ export default class AdminToolsService extends Service {
};
}
- _showControlModal(type, user, opts) {
+ @action
+ async showControlModal(type, user, opts) {
opts = opts || {};
-
- const controller = showModal(`admin-penalize-user`, {
- admin: true,
- modalClass: `${type}-user-modal`,
- });
-
- controller.setProperties({
- penaltyType: type,
- postId: opts.postId,
- postEdit: opts.postEdit,
- });
-
- return (
- user.adminUserView
- ? Promise.resolve(user)
- : AdminUser.find(user.get("id"))
- ).then((loadedUser) => {
- controller.setProperties({
+ const loadedUser = user.adminUserView
+ ? user
+ : await AdminUser.find(user.get("id"));
+ this.modal.show(PenalizeUserModal, {
+ model: {
+ penaltyType: type,
+ postId: opts.postId,
+ postEdit: opts.postEdit,
user: loadedUser,
- loadingUser: false,
before: opts.before,
successCallback: opts.successCallback,
- });
-
- controller.finishedSetup();
+ },
});
}
showSilenceModal(user, opts) {
- this._showControlModal("silence", user, opts);
+ this.showControlModal("silence", user, opts);
}
showSuspendModal(user, opts) {
- this._showControlModal("suspend", user, opts);
+ this.showControlModal("suspend", user, opts);
}
_deleteSpammer(adminUser) {
diff --git a/app/assets/javascripts/discourse/app/services/modal.js b/app/assets/javascripts/discourse/app/services/modal.js
index acd1b175119..0e4e076659b 100644
--- a/app/assets/javascripts/discourse/app/services/modal.js
+++ b/app/assets/javascripts/discourse/app/services/modal.js
@@ -45,7 +45,6 @@ const KNOWN_LEGACY_MODALS = [
"tag-upload",
"topic-summary",
"user-status",
- "admin-penalize-user",
"admin-reseed",
"admin-theme-item",
"admin-color-scheme-select-base",
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-penalize-user-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-penalize-user-test.js
index 5d88d81184c..41fd010af9a 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/admin-penalize-user-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-penalize-user-test.js
@@ -65,9 +65,9 @@ acceptance("Admin - Suspend User", function (needs) {
await click(".d-modal-cancel");
assert.strictEqual(count(".dialog-body:visible"), 1);
- assert.ok(!exists(".suspend-user-modal:visible"));
await click(".dialog-footer .btn-primary");
+ assert.ok(!exists(".suspend-user-modal:visible"));
assert.ok(!exists(".dialog-body:visible"));
});