diff --git a/apps/web/src/app/auth/settings/two-factor-email.component.html b/apps/web/src/app/auth/settings/two-factor-email.component.html index 93a6b0bb18..cf1dba9884 100644 --- a/apps/web/src/app/auth/settings/two-factor-email.component.html +++ b/apps/web/src/app/auth/settings/two-factor-email.component.html @@ -1,101 +1,53 @@ - + + 2. {{ "twoFactorEmailEnterCode" | i18n }} + + + + + + + + + + diff --git a/apps/web/src/app/auth/settings/two-factor-email.component.ts b/apps/web/src/app/auth/settings/two-factor-email.component.ts index 7a2e6de580..8a5c029223 100644 --- a/apps/web/src/app/auth/settings/two-factor-email.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-email.component.ts @@ -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 { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -19,18 +21,22 @@ import { TwoFactorBaseComponent } from "./two-factor-base.component"; @Component({ selector: "app-two-factor-email", templateUrl: "two-factor-email.component.html", + outputs: ["onUpdated"], }) export class TwoFactorEmailComponent extends TwoFactorBaseComponent { + @Output() onChangeStatus: EventEmitter = new EventEmitter(); type = TwoFactorProviderType.Email; - email: string; - token: string; sentEmail: string; formPromise: Promise; emailPromise: Promise; - override componentName = "app-two-factor-email"; + formGroup = this.formBuilder.group({ + token: [null], + email: ["", [Validators.email, Validators.required]], + }); constructor( + @Inject(DIALOG_DATA) protected data: AuthResponse, apiService: ApiService, i18nService: I18nService, platformUtilsService: PlatformUtilsService, @@ -38,6 +44,8 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent { userVerificationService: UserVerificationService, private accountService: AccountService, dialogService: DialogService, + private formBuilder: FormBuilder, + private dialogRef: DialogRef, ) { super( apiService, @@ -48,31 +56,49 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent { 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) { super.auth(authResponse); return this.processResponse(authResponse.response); } - submit() { + submit = async () => { if (this.enabled) { - return super.disable(this.formPromise); + await this.disableEmail(); + this.onChangeStatus.emit(false); } else { - return this.enable(); + await this.enable(); + this.onChangeStatus.emit(true); } + }; + + private disableEmail() { + return super.disable(this.formPromise); } - async sendEmail() { - try { - const request = await this.buildRequestModel(TwoFactorEmailRequest); - request.email = this.email; - this.emailPromise = this.apiService.postTwoFactorEmailSetup(request); - await this.emailPromise; - this.sentEmail = this.email; - } catch (e) { - this.logService.error(e); - } - } + sendEmail = async () => { + const request = await this.buildRequestModel(TwoFactorEmailRequest); + request.email = this.email; + this.emailPromise = this.apiService.postTwoFactorEmailSetup(request); + await this.emailPromise; + this.sentEmail = this.email; + }; protected async enable() { 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) { this.token = null; 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>, + ) { + return dialogService.open(TwoFactorEmailComponent, config); + } } diff --git a/apps/web/src/app/auth/settings/two-factor-setup.component.ts b/apps/web/src/app/auth/settings/two-factor-setup.component.ts index dc7871baf9..dd4c69aa27 100644 --- a/apps/web/src/app/auth/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-setup.component.ts @@ -1,3 +1,4 @@ +import { DialogRef } from "@angular/cdk/dialog"; import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core"; import { firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs"; @@ -178,11 +179,14 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { if (!result) { return; } - const emailComp = await this.openModal(this.emailModalRef, TwoFactorEmailComponent); - await emailComp.auth(result); - emailComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { - this.updateStatus(enabled, TwoFactorProviderType.Email); + const authComp: DialogRef = TwoFactorEmailComponent.open(this.dialogService, { + data: result, }); + authComp.componentInstance.onChangeStatus + .pipe(takeUntil(this.destroy$)) + .subscribe((enabled: boolean) => { + this.updateStatus(enabled, TwoFactorProviderType.Email); + }); break; } case TwoFactorProviderType.WebAuthn: {