1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 00:33:44 +00:00

[PM-2057] update two factor email dialog (#8974)

* migrating two factor email component

* two factor email component migration

* two factor email component migration

* two factor email component migration
This commit is contained in:
vinith-kovan
2024-06-05 22:29:51 +05:30
committed by GitHub
parent 419c107f87
commit 24fb3f71f1
3 changed files with 118 additions and 121 deletions

View File

@@ -1,28 +1,10 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="2faEmailTitle"> <form [formGroup]="formGroup" [bitSubmit]="submit" *ngIf="authed">
<div class="modal-dialog" role="document"> <bit-dialog>
<div class="modal-content"> <span bitDialogTitle>
<div class="modal-header">
<h1 class="modal-title" id="2faEmailTitle">
{{ "twoStepLogin" | i18n }} {{ "twoStepLogin" | i18n }}
<small>{{ "emailTitle" | i18n }}</small> <span bitTypography="body1">{{ "emailTitle" | i18n }}</span>
</h1> </span>
<button <ng-container bitDialogContent>
type="button"
class="close"
data-dismiss="modal"
appA11yTitle="{{ 'close' | i18n }}"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<form
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
*ngIf="authed"
>
<div class="modal-body">
<ng-container *ngIf="enabled"> <ng-container *ngIf="enabled">
<app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle"> <app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle">
{{ "twoStepLoginProviderEnabled" | i18n }} {{ "twoStepLoginProviderEnabled" | i18n }}
@@ -30,72 +12,42 @@
<strong>{{ "email" | i18n }}:</strong> {{ email }} <strong>{{ "email" | i18n }}:</strong> {{ email }}
</ng-container> </ng-container>
<ng-container *ngIf="!enabled"> <ng-container *ngIf="!enabled">
<p class="d-flex"> <p class="tw-flex">
<span class="mr-3">{{ "twoFactorEmailDesc" | i18n }}</span> <span class="tw-mr-3">{{ "twoFactorEmailDesc" | i18n }}</span>
<img class="float-right ml-auto mfaType1" alt="Email logo" /> <img class="tw-float-right tw-ml-auto mfaType1" alt="Email logo" />
</p> </p>
<div class="form-group"> <bit-form-field>
<label for="email">1. {{ "twoFactorEmailEnterEmail" | i18n }}</label> <bit-label>1. {{ "twoFactorEmailEnterEmail" | i18n }}</bit-label>
<input <input
id="email" bitInput
type="text" type="text"
name="Email" formControlName="email"
class="form-control"
[(ngModel)]="email"
required
inputmode="email" inputmode="email"
appInputVerbatim="false" appInputVerbatim="false"
/> />
</div> </bit-form-field>
<div class="mb-3 d-flex"> <div class="tw-mb-3 tw-flex">
<button <button bitButton type="button" buttonType="primary" [bitAction]="sendEmail">
#sendBtn {{ "sendEmail" | i18n }}
type="button"
class="btn btn-outline-primary btn-sm btn-submit align-self-start"
(click)="sendEmail()"
[appApiAction]="emailPromise"
[disabled]="$any(sendBtn).loading"
>
<i
class="bwi bwi-spinner bwi-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span>{{ "sendEmail" | i18n }}</span>
</button> </button>
<span class="text-success ml-3" *ngIf="sentEmail"> <span class="tw-text-success tw-ml-3" *ngIf="sentEmail">
{{ "verificationCodeEmailSent" | i18n: sentEmail }} {{ "verificationCodeEmailSent" | i18n: sentEmail }}
</span> </span>
</div> </div>
<div class="form-group"> <bit-form-field>
<label for="token">2. {{ "twoFactorEmailEnterCode" | i18n }}</label> <bit-label>2. {{ "twoFactorEmailEnterCode" | i18n }}</bit-label>
<input <input bitInput type="text" formControlName="token" appInputVerbatim />
id="token" </bit-form-field>
type="text"
name="Token"
class="form-control"
[(ngModel)]="token"
required
appInputVerbatim
/>
</div>
</ng-container> </ng-container>
</div> </ng-container>
<div class="modal-footer"> <ng-container bitDialogFooter>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading"> <button bitButton bitFormButton type="submit" buttonType="primary">
<i
class="bwi bwi-spinner bwi-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span *ngIf="!enabled">{{ "enable" | i18n }}</span> <span *ngIf="!enabled">{{ "enable" | i18n }}</span>
<span *ngIf="enabled">{{ "disable" | i18n }}</span> <span *ngIf="enabled">{{ "disable" | i18n }}</span>
</button> </button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal"> <button bitButton bitFormButton type="button" buttonType="secondary" bitDialogClose>
{{ "close" | i18n }} {{ "close" | i18n }}
</button> </button>
</div> </ng-container>
</form> </bit-dialog>
</div> </form>
</div>
</div>

View File

@@ -1,4 +1,6 @@
import { Component } from "@angular/core"; import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, EventEmitter, Inject, Output } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { firstValueFrom, map } from "rxjs"; import { firstValueFrom, map } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
@@ -19,18 +21,22 @@ import { TwoFactorBaseComponent } from "./two-factor-base.component";
@Component({ @Component({
selector: "app-two-factor-email", selector: "app-two-factor-email",
templateUrl: "two-factor-email.component.html", templateUrl: "two-factor-email.component.html",
outputs: ["onUpdated"],
}) })
export class TwoFactorEmailComponent extends TwoFactorBaseComponent { export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
@Output() onChangeStatus: EventEmitter<boolean> = new EventEmitter();
type = TwoFactorProviderType.Email; type = TwoFactorProviderType.Email;
email: string;
token: string;
sentEmail: string; sentEmail: string;
formPromise: Promise<TwoFactorEmailResponse>; formPromise: Promise<TwoFactorEmailResponse>;
emailPromise: Promise<unknown>; emailPromise: Promise<unknown>;
override componentName = "app-two-factor-email"; override componentName = "app-two-factor-email";
formGroup = this.formBuilder.group({
token: [null],
email: ["", [Validators.email, Validators.required]],
});
constructor( constructor(
@Inject(DIALOG_DATA) protected data: AuthResponse<TwoFactorEmailResponse>,
apiService: ApiService, apiService: ApiService,
i18nService: I18nService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
@@ -38,6 +44,8 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
userVerificationService: UserVerificationService, userVerificationService: UserVerificationService,
private accountService: AccountService, private accountService: AccountService,
dialogService: DialogService, dialogService: DialogService,
private formBuilder: FormBuilder,
private dialogRef: DialogRef,
) { ) {
super( super(
apiService, apiService,
@@ -48,31 +56,49 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
dialogService, dialogService,
); );
} }
get token() {
return this.formGroup.get("token").value;
}
set token(value: string) {
this.formGroup.get("token").setValue(value);
}
get email() {
return this.formGroup.get("email").value;
}
set email(value: string) {
this.formGroup.get("email").setValue(value);
}
async ngOnInit() {
await this.auth(this.data);
}
auth(authResponse: AuthResponse<TwoFactorEmailResponse>) { auth(authResponse: AuthResponse<TwoFactorEmailResponse>) {
super.auth(authResponse); super.auth(authResponse);
return this.processResponse(authResponse.response); return this.processResponse(authResponse.response);
} }
submit() { submit = async () => {
if (this.enabled) { if (this.enabled) {
return super.disable(this.formPromise); await this.disableEmail();
this.onChangeStatus.emit(false);
} else { } else {
return this.enable(); await this.enable();
this.onChangeStatus.emit(true);
} }
};
private disableEmail() {
return super.disable(this.formPromise);
} }
async sendEmail() { sendEmail = async () => {
try {
const request = await this.buildRequestModel(TwoFactorEmailRequest); const request = await this.buildRequestModel(TwoFactorEmailRequest);
request.email = this.email; request.email = this.email;
this.emailPromise = this.apiService.postTwoFactorEmailSetup(request); this.emailPromise = this.apiService.postTwoFactorEmailSetup(request);
await this.emailPromise; await this.emailPromise;
this.sentEmail = this.email; this.sentEmail = this.email;
} catch (e) { };
this.logService.error(e);
}
}
protected async enable() { protected async enable() {
const request = await this.buildRequestModel(UpdateTwoFactorEmailRequest); const request = await this.buildRequestModel(UpdateTwoFactorEmailRequest);
@@ -86,6 +112,10 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
}); });
} }
onClose = () => {
this.dialogRef.close(this.enabled);
};
private async processResponse(response: TwoFactorEmailResponse) { private async processResponse(response: TwoFactorEmailResponse) {
this.token = null; this.token = null;
this.email = response.email; this.email = response.email;
@@ -96,4 +126,15 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
); );
} }
} }
/**
* Strongly typed helper to open a TwoFactorEmailComponentComponent
* @param dialogService Instance of the dialog service that will be used to open the dialog
* @param config Configuration for the dialog
*/
static open(
dialogService: DialogService,
config: DialogConfig<AuthResponse<TwoFactorEmailResponse>>,
) {
return dialogService.open<boolean>(TwoFactorEmailComponent, config);
}
} }

View File

@@ -1,3 +1,4 @@
import { DialogRef } from "@angular/cdk/dialog";
import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core"; import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core";
import { firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs"; import { firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs";
@@ -178,9 +179,12 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
if (!result) { if (!result) {
return; return;
} }
const emailComp = await this.openModal(this.emailModalRef, TwoFactorEmailComponent); const authComp: DialogRef<boolean, any> = TwoFactorEmailComponent.open(this.dialogService, {
await emailComp.auth(result); data: result,
emailComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { });
authComp.componentInstance.onChangeStatus
.pipe(takeUntil(this.destroy$))
.subscribe((enabled: boolean) => {
this.updateStatus(enabled, TwoFactorProviderType.Email); this.updateStatus(enabled, TwoFactorProviderType.Email);
}); });
break; break;