1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 04:03:29 +00:00

[PM-18721] handle cross-component async actions

This commit is contained in:
rr-bw
2025-05-23 16:08:36 -07:00
parent 2a15b5cf63
commit cc5c60d374
3 changed files with 41 additions and 6 deletions

View File

@@ -28,7 +28,13 @@
</div>
<ng-container bitDialogFooter>
<button type="button" bitButton buttonType="primary" (click)="handlePrimaryButtonClick()">
<button
type="button"
bitButton
buttonType="primary"
[disabled]="submitting$ | async"
(click)="handlePrimaryButtonClick()"
>
{{ "save" | i18n }}
</button>
<button type="button" bitButton buttonType="secondary" bitDialogClose>

View File

@@ -1,6 +1,6 @@
import { CommonModule } from "@angular/common";
import { Component, Inject, OnInit, ViewChild } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { AfterViewInit, Component, Inject, OnInit, ViewChild } from "@angular/core";
import { BehaviorSubject, combineLatest, map } from "rxjs";
import {
InputPasswordComponent,
@@ -10,7 +10,6 @@ import {
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
@@ -61,10 +60,13 @@ type EmergencyAccessTakeoverDialogResultType =
InputPasswordComponent,
],
})
export class EmergencyAccessTakeoverDialogComponent implements OnInit {
export class EmergencyAccessTakeoverDialogComponent implements OnInit, AfterViewInit {
@ViewChild(InputPasswordComponent)
inputPasswordComponent!: InputPasswordComponent;
private submittingBehaviorSubject = new BehaviorSubject(false);
submitting$ = this.submittingBehaviorSubject.asObservable();
initializing = true;
inputPasswordFlow = InputPasswordFlow.ChangePasswordDelegation;
masterPasswordPolicyOptions?: MasterPasswordPolicyOptions;
@@ -93,11 +95,25 @@ export class EmergencyAccessTakeoverDialogComponent implements OnInit {
this.initializing = false;
}
ngAfterViewInit() {
this.submitting$ = combineLatest([
this.submittingBehaviorSubject.asObservable(),
this.inputPasswordComponent.submitting$,
]).pipe(
map(
([dialogCompIsSubmitting, inputPasswordCompIsSubmitting]) =>
dialogCompIsSubmitting || inputPasswordCompIsSubmitting,
),
);
}
protected handlePrimaryButtonClick = async () => {
await this.inputPasswordComponent.submit();
};
protected async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) {
this.submittingBehaviorSubject.next(true);
try {
await this.emergencyAccessService.takeover(
this.dialogData.emergencyAccessId,
@@ -112,6 +128,8 @@ export class EmergencyAccessTakeoverDialogComponent implements OnInit {
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("unexpectedError"),
});
} finally {
this.submittingBehaviorSubject.next(false);
}
this.dialogRef.close(EmergencyAccessTakeoverDialogResultTypes.Done);

View File

@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core";
import { ReactiveFormsModule, FormBuilder, Validators, FormControl } from "@angular/forms";
import { firstValueFrom } from "rxjs";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -116,6 +116,9 @@ interface InputPasswordForm {
],
})
export class InputPasswordComponent implements OnInit {
private submittingBehaviorSubject = new BehaviorSubject(false);
submitting$ = this.submittingBehaviorSubject.asObservable();
@ViewChild(PasswordStrengthV2Component) passwordStrengthComponent!: PasswordStrengthV2Component;
@Output() onPasswordFormSubmit = new EventEmitter<PasswordInputResult>();
@@ -258,12 +261,15 @@ export class InputPasswordComponent implements OnInit {
}
submit = async () => {
this.submittingBehaviorSubject.next(true);
this.verifyFlow();
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
this.showErrorSummary = true;
this.submittingBehaviorSubject.next(false);
return;
}
@@ -274,6 +280,7 @@ export class InputPasswordComponent implements OnInit {
if (this.flow === InputPasswordFlow.ChangePasswordDelegation) {
await this.handleChangePasswordDelegationFlow(newPassword);
this.submittingBehaviorSubject.next(false);
return;
}
@@ -305,6 +312,7 @@ export class InputPasswordComponent implements OnInit {
this.kdfConfig,
);
if (!currentPasswordVerified) {
this.submittingBehaviorSubject.next(false);
return;
}
}
@@ -316,6 +324,7 @@ export class InputPasswordComponent implements OnInit {
checkForBreaches,
);
if (!newPasswordVerified) {
this.submittingBehaviorSubject.next(false);
return;
}
@@ -381,6 +390,7 @@ export class InputPasswordComponent implements OnInit {
// 5. Emit cryptographic keys and other password related properties
this.onPasswordFormSubmit.emit(passwordInputResult);
this.submittingBehaviorSubject.next(false);
};
/**
@@ -436,6 +446,7 @@ export class InputPasswordComponent implements OnInit {
false,
);
if (!newPasswordVerified) {
this.submittingBehaviorSubject.next(false);
return;
}