mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 09:43:23 +00:00
Implements a new functionality for simple configurable dialogs that allows you to set an acceptAction which triggers a pending state. To use this set acceptAction to an async method, and it will be executed on accept prior to closing the dialog.
92 lines
2.7 KiB
TypeScript
92 lines
2.7 KiB
TypeScript
import { Directive, Input, OnDestroy, OnInit, Optional } from "@angular/core";
|
|
import { FormGroupDirective } from "@angular/forms";
|
|
import { BehaviorSubject, catchError, filter, of, Subject, switchMap, takeUntil } from "rxjs";
|
|
|
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
|
|
|
import { FunctionReturningAwaitable, functionToObservable } from "../utils/function-to-observable";
|
|
|
|
/**
|
|
* Allow a form to perform async actions on submit, disabling the form while the action is processing.
|
|
*/
|
|
@Directive({
|
|
selector: "[formGroup][bitSubmit]",
|
|
})
|
|
export class BitSubmitDirective implements OnInit, OnDestroy {
|
|
private destroy$ = new Subject<void>();
|
|
private _loading$ = new BehaviorSubject<boolean>(false);
|
|
private _disabled$ = new BehaviorSubject<boolean>(false);
|
|
|
|
@Input("bitSubmit") handler: FunctionReturningAwaitable;
|
|
|
|
@Input() allowDisabledFormSubmit?: boolean = false;
|
|
|
|
readonly loading$ = this._loading$.asObservable();
|
|
readonly disabled$ = this._disabled$.asObservable();
|
|
|
|
constructor(
|
|
private formGroupDirective: FormGroupDirective,
|
|
@Optional() validationService?: ValidationService,
|
|
@Optional() logService?: LogService
|
|
) {
|
|
formGroupDirective.ngSubmit
|
|
.pipe(
|
|
filter(() => !this.disabled),
|
|
switchMap(() => {
|
|
// Calling functionToObservable executes the sync part of the handler
|
|
// allowing the function to check form validity before it gets disabled.
|
|
const awaitable = functionToObservable(this.handler);
|
|
|
|
// Disable form
|
|
this.loading = true;
|
|
|
|
return awaitable.pipe(
|
|
catchError((err: unknown) => {
|
|
logService?.error(`Async submit exception: ${err}`);
|
|
validationService?.showError(err);
|
|
return of(undefined);
|
|
})
|
|
);
|
|
}),
|
|
takeUntil(this.destroy$)
|
|
)
|
|
.subscribe({
|
|
next: () => (this.loading = false),
|
|
complete: () => (this.loading = false),
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.formGroupDirective.statusChanges.pipe(takeUntil(this.destroy$)).subscribe((c) => {
|
|
if (this.allowDisabledFormSubmit) {
|
|
this._disabled$.next(false);
|
|
} else {
|
|
this._disabled$.next(c === "DISABLED");
|
|
}
|
|
});
|
|
}
|
|
|
|
get disabled() {
|
|
return this._disabled$.value;
|
|
}
|
|
|
|
set disabled(value: boolean) {
|
|
this._disabled$.next(value);
|
|
}
|
|
|
|
get loading() {
|
|
return this._loading$.value;
|
|
}
|
|
|
|
set loading(value: boolean) {
|
|
this.disabled = value;
|
|
this._loading$.next(value);
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.destroy$.next();
|
|
this.destroy$.complete();
|
|
}
|
|
}
|