DEV: migrate backup-codes to gjs (#27801)

Co-authored-by: Keegan George <kgeorge13@gmail.com>
This commit is contained in:
Joffrey JAFFEUX 2024-07-09 21:02:16 +02:00 committed by GitHub
parent 8cc1d9771b
commit 5fc7c79d8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 98 deletions

View File

@ -0,0 +1,100 @@
import Component from "@glimmer/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import copyText from "discourse/lib/copy-text";
import { slugify, toAsciiPrintable } from "discourse/lib/utilities";
import i18n from "discourse-common/helpers/i18n";
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
function b64EncodeUnicode(str) {
return btoa(
encodeURIComponent(str).replace(
/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode("0x" + p1);
}
)
);
}
export default class BackupCodes extends Component {
@service siteSettings;
get siteTitleSlug() {
const title = this.siteSettings.title;
const convertedTitle = toAsciiPrintable(title, "discourse");
return slugify(convertedTitle);
}
get base64BackupCode() {
return b64EncodeUnicode(this.formattedBackupCodes);
}
get formattedBackupCodes() {
if (!this.args.backupCodes) {
return null;
}
return this.args.backupCodes.join("\n").trim();
}
@action
copyToClipboard() {
this._selectAllBackupCodes();
const copied = copyText("", this.backupCodesArea);
this.args.copyBackupCode(copied);
}
@action
registerBackupCodesArea(element) {
this.backupCodesArea = element;
element.style.height = element.scrollHeight;
}
@action
_selectAllBackupCodes() {
this.backupCodesArea.focus();
this.backupCodesArea.setSelectionRange(0, this.formattedBackupCodes.length);
}
<template>
<div class="backup-codes">
<div class="wrapper">
<textarea
id="backupCodes"
class="backup-codes-area"
rows="10"
readonly
{{didInsert this.registerBackupCodesArea}}
{{on "click" this._selectAllBackupCodes}}
>{{this.formattedBackupCodes}}</textarea>
<div class="controls">
<DButton
@action={{this.copyToClipboard}}
@icon="copy"
@ariaLabel="user.second_factor_backup.copy_to_clipboard"
@title="user.second_factor_backup.copy_to_clipboard"
class="backup-codes-copy-btn"
/>
<DButton
download="{{this.siteTitleSlug}}-backup-codes.txt"
class="backup-codes-download-btn"
aria-label={{i18n
"user.second_factor_backup.download_backup_codes"
}}
title={{i18n "user.second_factor_backup.download_backup_codes"}}
rel="noopener noreferrer"
target="_blank"
@href="data:application/octet-stream;charset=utf-8;base64,{{this.base64BackupCode}}"
@icon="download"
/>
</div>
</div>
</div>
</template>
}

View File

@ -1,30 +0,0 @@
<div class="wrapper">
<textarea
id="backupCodes"
class="backup-codes-area"
rows="10"
readonly
>{{this.formattedBackupCodes}}</textarea>
<div class="controls">
<DButton
@action={{action "copyToClipboard"}}
@icon="copy"
@ariaLabel="user.second_factor_backup.copy_to_clipboard"
@title="user.second_factor_backup.copy_to_clipboard"
class="btn-default backup-codes-copy-btn"
/>
<a
download="{{this.siteTitleSlug}}-backup-codes.txt"
class="btn btn-default no-text btn-icon backup-codes-download-btn"
aria-label={{i18n "user.second_factor_backup.download_backup_codes"}}
title={{i18n "user.second_factor_backup.download_backup_codes"}}
rel="noopener noreferrer"
target="_blank"
href="data:application/octet-stream;charset=utf-8;base64,{{this.base64BackupCode}}"
>
{{d-icon "download"}}
</a>
</div>
</div>

View File

@ -1,68 +0,0 @@
import Component from "@ember/component";
import { slugify, toAsciiPrintable } from "discourse/lib/utilities";
import discourseComputed from "discourse-common/utils/decorators";
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
function b64EncodeUnicode(str) {
return btoa(
encodeURIComponent(str).replace(
/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode("0x" + p1);
}
)
);
}
export default Component.extend({
classNames: ["backup-codes"],
backupCodes: null,
click(event) {
if (event.target.id === "backupCodes") {
this._selectAllBackupCodes();
}
},
didRender() {
this._super(...arguments);
const backupCodes = this.element.querySelector("#backupCodes");
if (backupCodes) {
backupCodes.style.height = backupCodes.scrollHeight;
}
},
@discourseComputed("formattedBackupCodes")
base64BackupCode: b64EncodeUnicode,
@discourseComputed("backupCodes")
formattedBackupCodes(backupCodes) {
if (!backupCodes) {
return null;
}
return backupCodes.join("\n").trim();
},
@discourseComputed()
siteTitleSlug() {
const title = this.siteSettings.title;
const convertedTitle = toAsciiPrintable(title, "discourse");
return slugify(convertedTitle);
},
actions: {
copyToClipboard() {
this._selectAllBackupCodes();
this.copyBackupCode(document.execCommand("copy"));
},
},
_selectAllBackupCodes() {
const textArea = this.element.querySelector("#backupCodes");
textArea.focus();
textArea.setSelectionRange(0, this.formattedBackupCodes.length);
},
});