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
This commit is contained in:
parent
f92ed88c52
commit
c05e54e461
|
@ -0,0 +1,62 @@
|
||||||
|
<DModal
|
||||||
|
@title={{i18n "user.second_factor_backup.title"}}
|
||||||
|
@closeModal={{@closeModal}}
|
||||||
|
class="second-factor-backup-edit-modal"
|
||||||
|
>
|
||||||
|
<:body>
|
||||||
|
{{#if this.successMessage}}
|
||||||
|
<div class="alert alert-success">
|
||||||
|
{{this.successMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.errorMessage}}
|
||||||
|
<div class="alert alert-error">
|
||||||
|
{{this.errorMessage}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<ConditionalLoadingSection @isLoading={{this.loading}}>
|
||||||
|
{{#if this.backupCodes}}
|
||||||
|
<h3>{{i18n "user.second_factor_backup.codes.title"}}</h3>
|
||||||
|
<p>{{i18n "user.second_factor_backup.codes.description"}}</p>
|
||||||
|
<BackupCodes
|
||||||
|
@copyBackupCode={{this.copyBackupCode}}
|
||||||
|
@backupCodes={{this.backupCodes}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</ConditionalLoadingSection>
|
||||||
|
|
||||||
|
{{#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}}
|
||||||
|
</:body>
|
||||||
|
<:footer>
|
||||||
|
<div class="actions">
|
||||||
|
{{#if this.backupEnabled}}
|
||||||
|
<DButton
|
||||||
|
class="btn-primary"
|
||||||
|
@icon="redo"
|
||||||
|
@action={{this.generateSecondFactorCodes}}
|
||||||
|
@type="submit"
|
||||||
|
@isLoading={{this.loading}}
|
||||||
|
@label="user.second_factor_backup.regenerate"
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
class="btn-primary"
|
||||||
|
@action={{this.generateSecondFactorCodes}}
|
||||||
|
@type="submit"
|
||||||
|
@disabled={{this.loading}}
|
||||||
|
@label="user.second_factor_backup.enable"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</:footer>
|
||||||
|
</DModal>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,13 +8,13 @@ import { alias } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { findAll } from "discourse/models/login-method";
|
import { findAll } from "discourse/models/login-method";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import showModal from "discourse/lib/show-modal";
|
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import SecondFactorConfirmPhrase from "discourse/components/dialog-messages/second-factor-confirm-phrase";
|
import SecondFactorConfirmPhrase from "discourse/components/dialog-messages/second-factor-confirm-phrase";
|
||||||
import SecondFactorAddSecurityKey from "discourse/components/modal/second-factor-add-security-key";
|
import SecondFactorAddSecurityKey from "discourse/components/modal/second-factor-add-security-key";
|
||||||
import SecondFactorEditSecurityKey from "discourse/components/modal/second-factor-edit-security-key";
|
import SecondFactorEditSecurityKey from "discourse/components/modal/second-factor-edit-security-key";
|
||||||
import SecondFactorEdit from "discourse/components/modal/second-factor-edit";
|
import SecondFactorEdit from "discourse/components/modal/second-factor-edit";
|
||||||
import SecondFactorAddTotp from "discourse/components/modal/second-factor-add-totp";
|
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, {
|
export default Controller.extend(CanCheckEmails, {
|
||||||
dialog: service(),
|
dialog: service(),
|
||||||
|
@ -63,6 +63,11 @@ export default Controller.extend(CanCheckEmails, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
setBackupEnabled(value) {
|
||||||
|
this.set("backupEnabled", value);
|
||||||
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
loadSecondFactors() {
|
loadSecondFactors() {
|
||||||
if (this.dirty === false) {
|
if (this.dirty === false) {
|
||||||
|
@ -310,15 +315,14 @@ export default Controller.extend(CanCheckEmails, {
|
||||||
this.loadSecondFactors();
|
this.loadSecondFactors();
|
||||||
},
|
},
|
||||||
|
|
||||||
editSecondFactorBackup() {
|
async editSecondFactorBackup() {
|
||||||
const controller = showModal("second-factor-backup-edit", {
|
await this.modal.show(SecondFactorBackupEdit, {
|
||||||
model: this.model,
|
model: {
|
||||||
title: "user.second_factor_backup.title",
|
secondFactor: this.model,
|
||||||
});
|
|
||||||
controller.setProperties({
|
|
||||||
onClose: () => this.loadSecondFactors(),
|
|
||||||
markDirty: () => this.markDirty(),
|
markDirty: () => this.markDirty(),
|
||||||
onError: (e) => this.handleError(e),
|
onError: (e) => this.handleError(e),
|
||||||
|
setBackupEnabled: (e) => this.setBackupEnabled(e),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,61 +0,0 @@
|
||||||
<DModalBody>
|
|
||||||
{{#if this.successMessage}}
|
|
||||||
<div class="alert alert-success">
|
|
||||||
{{this.successMessage}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.errorMessage}}
|
|
||||||
<div class="alert alert-error">
|
|
||||||
{{this.errorMessage}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<ConditionalLoadingSection @isLoading={{this.loading}}>
|
|
||||||
{{#if this.backupCodes}}
|
|
||||||
<h3>{{i18n "user.second_factor_backup.codes.title"}}</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{i18n "user.second_factor_backup.codes.description"}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<BackupCodes
|
|
||||||
@copyBackupCode={{action "copyBackupCode"}}
|
|
||||||
@backupCodes={{this.backupCodes}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</ConditionalLoadingSection>
|
|
||||||
|
|
||||||
{{#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}}
|
|
||||||
</DModalBody>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="actions">
|
|
||||||
{{#if this.backupEnabled}}
|
|
||||||
<DButton
|
|
||||||
@class="btn-primary"
|
|
||||||
@icon="redo"
|
|
||||||
@action={{action "generateSecondFactorCodes"}}
|
|
||||||
@type="submit"
|
|
||||||
@isLoading={{this.loading}}
|
|
||||||
@label="user.second_factor_backup.regenerate"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
@class="btn-primary"
|
|
||||||
@action={{action "generateSecondFactorCodes"}}
|
|
||||||
@type="submit"
|
|
||||||
@disabled={{this.loading}}
|
|
||||||
@label="user.second_factor_backup.enable"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -48,12 +48,10 @@ acceptance("User Preferences - Second Factor Backup", function (needs) {
|
||||||
updateCurrentUser({ second_factor_enabled: true });
|
updateCurrentUser({ second_factor_enabled: true });
|
||||||
await visit("/u/eviltrout/preferences/second-factor");
|
await visit("/u/eviltrout/preferences/second-factor");
|
||||||
|
|
||||||
if (exists(".new-second-factor-backup")) {
|
// create backup codes
|
||||||
// if codes don't exist yet, create them
|
|
||||||
await click(".new-second-factor-backup");
|
await click(".new-second-factor-backup");
|
||||||
await click(".second-factor-backup-edit-modal .btn-primary");
|
await click(".second-factor-backup-edit-modal .btn-primary");
|
||||||
await click(".modal-close");
|
await click(".second-factor-backup-edit-modal .modal-close");
|
||||||
}
|
|
||||||
|
|
||||||
await click(".two-factor-backup-dropdown .select-kit-header");
|
await click(".two-factor-backup-dropdown .select-kit-header");
|
||||||
await click("li[data-name='Disable'");
|
await click("li[data-name='Disable'");
|
||||||
|
|
Loading…
Reference in New Issue