1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 05:53:42 +00:00

[Account Recovery][PM-18721] handle cross-component submit process

This commit is contained in:
rr-bw
2025-05-28 14:46:49 -07:00
parent 09128e2407
commit 1fccdde5b8
2 changed files with 23 additions and 29 deletions

View File

@@ -9,27 +9,19 @@
[message]="'resetPasswordMasterPasswordPolicyInEffect'"
[masterPasswordPolicyOptions]="masterPasswordPolicyOptions$ | async"
(onPasswordFormSubmit)="handlePasswordFormSubmit($event)"
(isSubmitting)="handleIsSubmittingChange($event)"
></auth-input-password>
</ng-container>
<ng-container bitDialogFooter>
<button
class="tw-relative"
type="button"
bitButton
buttonType="primary"
[disabled]="submitting"
[disabled]="submitting$ | async"
(click)="handlePrimaryButtonClick()"
>
<span
[ngClass]="{ 'tw-invisible': !submitting }"
class="tw-absolute tw-inset-0 tw-flex tw-items-center tw-justify-center"
>
<i class="bwi bwi-spinner bwi-lg bwi-spin" aria-hidden="true"></i>
</span>
<span [ngClass]="{ 'tw-invisible': submitting }">
{{ "save" | i18n }}
</span>
{{ "save" | i18n }}
</button>
<button type="button" bitButton buttonType="secondary" bitDialogClose>
{{ "cancel" | i18n }}

View File

@@ -1,6 +1,6 @@
import { CommonModule } from "@angular/common";
import { Component, Inject, ViewChild } from "@angular/core";
import { switchMap } from "rxjs";
import { BehaviorSubject, combineLatest, map, switchMap } from "rxjs";
import {
InputPasswordComponent,
@@ -82,15 +82,23 @@ export class AccountRecoveryDialogComponent {
@ViewChild(InputPasswordComponent)
inputPasswordComponent!: InputPasswordComponent;
inputPasswordFlow = InputPasswordFlow.ChangePasswordDelegation;
receivedPasswordInputResult = false;
submitting = false;
private parentSubmittingBehaviorSubject = new BehaviorSubject(false);
parentSubmitting$ = this.parentSubmittingBehaviorSubject.asObservable();
private childSubmittingBehaviorSubject = new BehaviorSubject(false);
childSubmitting$ = this.childSubmittingBehaviorSubject.asObservable();
submitting$ = combineLatest([this.parentSubmitting$, this.childSubmitting$]).pipe(
map(([parentIsSubmitting, childIsSubmitting]) => parentIsSubmitting || childIsSubmitting),
);
masterPasswordPolicyOptions$ = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
);
inputPasswordFlow = InputPasswordFlow.ChangePasswordDelegation;
get loggedOutWarningName() {
return this.dialogData.name != null ? this.dialogData.name : this.i18nService.t("thisUser");
}
@@ -107,23 +115,11 @@ export class AccountRecoveryDialogComponent {
) {}
handlePrimaryButtonClick = async () => {
try {
this.submitting = true;
await this.inputPasswordComponent.submit();
} catch {
// Flip to false if submit() throws an error
this.submitting = false;
}
// Flip to false if submit() returns early without a PasswordInputResult
// emission due to form validation errors or new password doesn't meet org policy reqs.
if (!this.receivedPasswordInputResult) {
this.submitting = false;
}
await this.inputPasswordComponent.submit();
};
async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) {
this.receivedPasswordInputResult = Boolean(passwordInputResult);
this.parentSubmittingBehaviorSubject.next(true);
try {
await this.resetPasswordService.resetMasterPassword(
@@ -140,11 +136,17 @@ export class AccountRecoveryDialogComponent {
});
} catch (e) {
this.logService.error(e);
} finally {
this.parentSubmittingBehaviorSubject.next(false);
}
this.dialogRef.close(AccountRecoveryDialogResultTypes.Ok);
}
protected handleIsSubmittingChange(isSubmitting: boolean) {
this.childSubmittingBehaviorSubject.next(isSubmitting);
}
/**
* Strongly typed helper to open an `AccountRecoveryDialogComponent`
* @param dialogService Instance of the dialog service that will be used to open the dialog