From c05e54e4610d05c2ff6081994c1511e294044908 Mon Sep 17 00:00:00 2001 From: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:03:18 -0500 Subject: [PATCH] DEV: Convert second-factor-backup-edit modal to component-based API (#22366) This PR converts the `second-factor-backup-edit` modal to make use of the new component-based API --- .../modal/second-factor-backup-edit.hbs | 62 +++++++++++++++ .../modal/second-factor-backup-edit.js | 63 +++++++++++++++ .../controllers/preferences/second-factor.js | 24 +++--- .../controllers/second-factor-backup-edit.js | 79 ------------------- .../modal/second-factor-backup-edit.hbs | 61 -------------- ...r-preferences-second-factor-backup-test.js | 10 +-- 6 files changed, 143 insertions(+), 156 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.hbs create mode 100644 app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.js delete mode 100644 app/assets/javascripts/discourse/app/controllers/second-factor-backup-edit.js delete mode 100644 app/assets/javascripts/discourse/app/templates/modal/second-factor-backup-edit.hbs diff --git a/app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.hbs b/app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.hbs new file mode 100644 index 00000000000..66b315c86d3 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.hbs @@ -0,0 +1,62 @@ + + <:body> + {{#if this.successMessage}} +
+ {{this.successMessage}} +
+ {{/if}} + + {{#if this.errorMessage}} +
+ {{this.errorMessage}} +
+ {{/if}} + + + {{#if this.backupCodes}} +

{{i18n "user.second_factor_backup.codes.title"}}

+

{{i18n "user.second_factor_backup.codes.description"}}

+ + {{/if}} +
+ + {{#if this.backupEnabled}} + {{html-safe + (i18n + "user.second_factor_backup.remaining_codes" count=this.remainingCodes + ) + }} + {{else}} + {{html-safe (i18n "user.second_factor_backup.not_enabled")}} + {{/if}} + + <:footer> +
+ {{#if this.backupEnabled}} + + {{else}} + + {{/if}} +
+ +
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.js b/app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.js new file mode 100644 index 00000000000..dcfbb09fa26 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/second-factor-backup-edit.js @@ -0,0 +1,63 @@ +import Component from "@glimmer/component"; +import I18n from "I18n"; +import { SECOND_FACTOR_METHODS } from "discourse/models/user"; +import { debounce } from "discourse-common/utils/decorators"; +import { tracked } from "@glimmer/tracking"; +import { action } from "@ember/object"; + +export default class SecondFactorBackupEdit extends Component { + @tracked loading = false; + @tracked errorMessage; + @tracked successMessage; + @tracked backupEnabled = + this.args.model.secondFactor.second_factor_backup_enabled; + @tracked remainingCodes = + this.args.model.secondFactor.second_factor_remaining_backup_codes; + @tracked backupCodes; + @tracked secondFactorMethod = SECOND_FACTOR_METHODS.TOTP; + + @action + copyBackupCode(successful) { + if (successful) { + this.successMessage = I18n.t( + "user.second_factor_backup.copied_to_clipboard" + ); + } else { + this.errorMessage = I18n.t( + "user.second_factor_backup.copy_to_clipboard_error" + ); + } + this._hideCopyMessage(); + } + + @action + generateSecondFactorCodes() { + this.loading = true; + this.args.model.secondFactor + .generateSecondFactorCodes() + .then((response) => { + if (response.error) { + this.errorMessage = response.error; + return; + } + + this.args.model.markDirty(); + this.errorMessage = null; + this.backupCodes = response.backup_codes; + this.args.model.setBackupEnabled(true); + this.backupEnabled = true; + this.remainingCodes = response.backup_codes.length; + }) + .catch((error) => { + this.args.closeModal(); + this.args.model.onError(error); + }) + .finally(() => (this.loading = false)); + } + + @debounce(2000) + _hideCopyMessage() { + this.successMessage = null; + this.errorMessage = null; + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js b/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js index 0d95914b415..6f62530195c 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js @@ -8,13 +8,13 @@ import { alias } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import { findAll } from "discourse/models/login-method"; import { popupAjaxError } from "discourse/lib/ajax-error"; -import showModal from "discourse/lib/show-modal"; import { inject as service } from "@ember/service"; import SecondFactorConfirmPhrase from "discourse/components/dialog-messages/second-factor-confirm-phrase"; import SecondFactorAddSecurityKey from "discourse/components/modal/second-factor-add-security-key"; import SecondFactorEditSecurityKey from "discourse/components/modal/second-factor-edit-security-key"; import SecondFactorEdit from "discourse/components/modal/second-factor-edit"; import SecondFactorAddTotp from "discourse/components/modal/second-factor-add-totp"; +import SecondFactorBackupEdit from "discourse/components/modal/second-factor-backup-edit"; export default Controller.extend(CanCheckEmails, { dialog: service(), @@ -63,6 +63,11 @@ export default Controller.extend(CanCheckEmails, { } }, + @action + setBackupEnabled(value) { + this.set("backupEnabled", value); + }, + @action loadSecondFactors() { if (this.dirty === false) { @@ -310,15 +315,14 @@ export default Controller.extend(CanCheckEmails, { this.loadSecondFactors(); }, - editSecondFactorBackup() { - const controller = showModal("second-factor-backup-edit", { - model: this.model, - title: "user.second_factor_backup.title", - }); - controller.setProperties({ - onClose: () => this.loadSecondFactors(), - markDirty: () => this.markDirty(), - onError: (e) => this.handleError(e), + async editSecondFactorBackup() { + await this.modal.show(SecondFactorBackupEdit, { + model: { + secondFactor: this.model, + markDirty: () => this.markDirty(), + onError: (e) => this.handleError(e), + setBackupEnabled: (e) => this.setBackupEnabled(e), + }, }); }, }, diff --git a/app/assets/javascripts/discourse/app/controllers/second-factor-backup-edit.js b/app/assets/javascripts/discourse/app/controllers/second-factor-backup-edit.js deleted file mode 100644 index 6854102fe0b..00000000000 --- a/app/assets/javascripts/discourse/app/controllers/second-factor-backup-edit.js +++ /dev/null @@ -1,79 +0,0 @@ -import Controller from "@ember/controller"; -import I18n from "I18n"; -import ModalFunctionality from "discourse/mixins/modal-functionality"; -import { SECOND_FACTOR_METHODS } from "discourse/models/user"; -import { alias } from "@ember/object/computed"; -import discourseLater from "discourse-common/lib/later"; - -export default Controller.extend(ModalFunctionality, { - loading: false, - errorMessage: null, - successMessage: null, - backupEnabled: alias("model.second_factor_backup_enabled"), - remainingCodes: alias("model.second_factor_remaining_backup_codes"), - backupCodes: null, - secondFactorMethod: SECOND_FACTOR_METHODS.TOTP, - - onShow() { - this.setProperties({ - loading: false, - errorMessage: null, - successMessage: null, - backupCodes: null, - }); - }, - - actions: { - copyBackupCode(successful) { - if (successful) { - this.set( - "successMessage", - I18n.t("user.second_factor_backup.copied_to_clipboard") - ); - } else { - this.set( - "errorMessage", - I18n.t("user.second_factor_backup.copy_to_clipboard_error") - ); - } - - this._hideCopyMessage(); - }, - - generateSecondFactorCodes() { - this.set("loading", true); - this.model - .generateSecondFactorCodes() - .then((response) => { - if (response.error) { - this.set("errorMessage", response.error); - return; - } - - this.markDirty(); - this.setProperties({ - errorMessage: null, - backupCodes: response.backup_codes, - backupEnabled: true, - remainingCodes: response.backup_codes.length, - }); - }) - .catch((error) => { - this.send("closeModal"); - this.onError(error); - }) - .finally(() => { - this.setProperties({ - loading: false, - }); - }); - }, - }, - - _hideCopyMessage() { - discourseLater( - () => this.setProperties({ successMessage: null, errorMessage: null }), - 2000 - ); - }, -}); diff --git a/app/assets/javascripts/discourse/app/templates/modal/second-factor-backup-edit.hbs b/app/assets/javascripts/discourse/app/templates/modal/second-factor-backup-edit.hbs deleted file mode 100644 index 1e1633ef87e..00000000000 --- a/app/assets/javascripts/discourse/app/templates/modal/second-factor-backup-edit.hbs +++ /dev/null @@ -1,61 +0,0 @@ - - {{#if this.successMessage}} -
- {{this.successMessage}} -
- {{/if}} - - {{#if this.errorMessage}} -
- {{this.errorMessage}} -
- {{/if}} - - - {{#if this.backupCodes}} -

{{i18n "user.second_factor_backup.codes.title"}}

- -

- {{i18n "user.second_factor_backup.codes.description"}} -

- - - {{/if}} -
- - {{#if this.backupEnabled}} - {{html-safe - (i18n - "user.second_factor_backup.remaining_codes" count=this.remainingCodes - ) - }} - {{else}} - {{html-safe (i18n "user.second_factor_backup.not_enabled")}} - {{/if}} -
- - \ No newline at end of file diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-second-factor-backup-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-second-factor-backup-test.js index 71642244377..1da4c77afd7 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-second-factor-backup-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-second-factor-backup-test.js @@ -48,12 +48,10 @@ acceptance("User Preferences - Second Factor Backup", function (needs) { updateCurrentUser({ second_factor_enabled: true }); await visit("/u/eviltrout/preferences/second-factor"); - if (exists(".new-second-factor-backup")) { - // if codes don't exist yet, create them - await click(".new-second-factor-backup"); - await click(".second-factor-backup-edit-modal .btn-primary"); - await click(".modal-close"); - } + // create backup codes + await click(".new-second-factor-backup"); + await click(".second-factor-backup-edit-modal .btn-primary"); + await click(".second-factor-backup-edit-modal .modal-close"); await click(".two-factor-backup-dropdown .select-kit-header"); await click("li[data-name='Disable'");