UI: Add action print backup code

This commit is contained in:
Vladyslav_Prykhodko 2022-05-26 09:41:42 +03:00
parent 7af89eefb0
commit 152f5bc2a8
6 changed files with 90 additions and 8 deletions

View File

@ -80,10 +80,10 @@
max-width: 500px; max-width: 500px;
.container { .container {
max-width: 500px; max-width: 500px;
margin: 40px 0 24px; margin: 40px 0 8px;
.code { .code {
letter-spacing: 0.25px; letter-spacing: 0.25px;
padding: 0 30px; padding: 0 24px;
margin-bottom: 16px; margin-bottom: 16px;
font-family: Roboto Mono, "Helvetica Neue", monospace; font-family: Roboto Mono, "Helvetica Neue", monospace;
&.even { &.even {
@ -91,6 +91,9 @@
} }
} }
} }
.action-buttons {
margin-bottom: 40px;
}
} }
& ::ng-deep { & ::ng-deep {

View File

@ -34,13 +34,18 @@
{{ code }} {{ code }}
</div> </div>
</div> </div>
<p class="mat-body-1 description" translate>security.2fa.dialog.backup-code-warn</p> <div fxLayout="row" fxLayoutAlign="center start" fxLayoutGap="16px" class="action-buttons">
<div fxLayout="row" fxLayoutAlign="center start" fxLayoutGap="16px">
<button type="button" mat-stroked-button color="primary" (click)="downloadFile()"> <button type="button" mat-stroked-button color="primary" (click)="downloadFile()">
{{ 'security.2fa.dialog.download-txt' | translate }} {{ 'security.2fa.dialog.download-txt' | translate }}
</button> </button>
<button type="button" mat-raised-button color="primary"> <button type="button" mat-raised-button color="primary" (click)="printCode()">
{{ 'action.print' | translate }} {{ 'action.print' | translate }}
</button> </button>
</div> </div>
<p class="mat-body-1 description" translate>security.2fa.dialog.backup-code-warn</p>
<div fxLayout="row" fxLayoutAlign="end start">
<button type="button" mat-raised-button color="primary" (click)="closeDialog()">
{{ 'action.done' | translate }}
</button>
</div>
</div> </div>

View File

@ -29,6 +29,9 @@ import {
} from '@shared/models/two-factor-auth.models'; } from '@shared/models/two-factor-auth.models';
import { mergeMap, tap } from 'rxjs/operators'; import { mergeMap, tap } from 'rxjs/operators';
import { ImportExportService } from '@home/components/import-export/import-export.service'; import { ImportExportService } from '@home/components/import-export/import-export.service';
import { deepClone } from '@core/utils';
import printTemplate from '!raw-loader!./backup-code-print-template.raw';
@Component({ @Component({
selector: 'tb-backup-code-auth-dialog', selector: 'tb-backup-code-auth-dialog',
@ -62,4 +65,24 @@ export class BackupCodeAuthDialogComponent extends DialogComponent<BackupCodeAut
downloadFile() { downloadFile() {
this.importExportService.exportText(this.backupCode.codes, 'backup-codes'); this.importExportService.exportText(this.backupCode.codes, 'backup-codes');
} }
printCode() {
const codeTemplate = deepClone(this.backupCode.codes)
.map(code => `<div class="code-row"><input type="checkbox"><span class="code">${code}</span></div>`).join('');
const printPage = printTemplate.replace('${codesBlock}', codeTemplate);
const newWindow = window.open('', 'Print backup code');
newWindow.document.open();
newWindow.document.write(printPage);
setTimeout(() => {
newWindow.print();
newWindow.document.close();
setTimeout(() => {
newWindow.close();
}, 10);
}, 0);
}
} }

View File

@ -0,0 +1,50 @@
<html lang="en">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Backup code</title>
<style>
.code-block {
display: flex;
flex-wrap: wrap;
margin-bottom: 16px;
}
.code-row {
margin: 0 6px 8px;
display: flex;
min-width: 130px;
}
.code {
font: 400 16px / 20px Roboto Mono, "Helvetica Neue", monospace;
margin-left: 6px;
}
input[type="checkbox"] {
-webkit-appearance: none;
appearance: none;
background-color: #fff;
margin: 0;
font: inherit;
color: currentColor;
width: 1em;
height: 1em;
border: 0.1em solid currentColor;
border-radius: 0.15em;
transform: translateY(0em);
}
</style>
</head>
<body style="margin: 0">
<div style="margin: 8px; max-width: 286px">
<div style="border: #d0d7de solid 1px; border-radius:4px">
<h3 style="padding: 16px 24px; margin: 0; font: 500 20px / 24px Roboto, 'Helvetica Neue', sans-serif; text-align: center">
Backup codes
</h3>
<div class="code-block">
${codesBlock}
</div>
</div>
</div>
</body>
</html>

View File

@ -66,15 +66,15 @@
<mat-checkbox [value]="provider" <mat-checkbox [value]="provider"
[checked]="useByDefault === provider" [checked]="useByDefault === provider"
(click)="changeDefaultProvider($event, provider)" (click)="changeDefaultProvider($event, provider)"
[disabled]="(isLoading$ | async) || activeSingleProvider" [disabled]="(isLoading$ | async)"
*ngIf="twoFactorAuth.get(provider).value && provider !== twoFactorAuthProviderType.BACKUP_CODE"> *ngIf="twoFactorAuth.get(provider).value && provider !== twoFactorAuthProviderType.BACKUP_CODE && !activeSingleProvider">
<span class="checkbox-label" translate>security.2fa.main-2fa-method</span> <span class="checkbox-label" translate>security.2fa.main-2fa-method</span>
</mat-checkbox> </mat-checkbox>
<button type="button" <button type="button"
mat-stroked-button color="primary" mat-stroked-button color="primary"
(click)="generateNewBackupCode()" (click)="generateNewBackupCode()"
*ngIf="twoFactorAuth.get(provider).value && provider === twoFactorAuthProviderType.BACKUP_CODE"> *ngIf="twoFactorAuth.get(provider).value && provider === twoFactorAuthProviderType.BACKUP_CODE">
Get new code {{ 'security.2fa.get-new-code' | translate }}
</button> </button>
</div> </div>
<mat-divider *ngIf="!$last"></mat-divider> <mat-divider *ngIf="!$last"></mat-divider>

View File

@ -2590,6 +2590,7 @@
"authenticate-with": "You can authenticate with:", "authenticate-with": "You can authenticate with:",
"disable-2fa-provider-text": "Disabling {{name}} will make your account less secure", "disable-2fa-provider-text": "Disabling {{name}} will make your account less secure",
"disable-2fa-provider-title": "Are you sure you want to disable {{name}}?", "disable-2fa-provider-title": "Are you sure you want to disable {{name}}?",
"get-new-code": "Get new code",
"main-2fa-method": "Use as main two-factor authentication method", "main-2fa-method": "Use as main two-factor authentication method",
"dialog": { "dialog": {
"activation-step-description-email": "The next time you login in, you will be prompted to enter the security code that will be sent to your email address.", "activation-step-description-email": "The next time you login in, you will be prompted to enter the security code that will be sent to your email address.",