import { DEFAULT_DIALOG_CONFIG, Dialog, DialogConfig, DialogRef, DIALOG_SCROLL_STRATEGY, } from "@angular/cdk/dialog"; import { ComponentType, Overlay, OverlayContainer } from "@angular/cdk/overlay"; import { Inject, Injectable, Injector, OnDestroy, Optional, SkipSelf, TemplateRef, } from "@angular/core"; import { NavigationEnd, Router } from "@angular/router"; import { filter, firstValueFrom, Subject, switchMap, takeUntil } from "rxjs"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SimpleConfigurableDialogComponent } from "./simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component"; import { SimpleDialogOptions, Translation } from "./simple-dialog/types"; @Injectable() export class DialogService extends Dialog implements OnDestroy { private _destroy$ = new Subject(); private backDropClasses = ["tw-fixed", "tw-bg-black", "tw-bg-opacity-30", "tw-inset-0"]; constructor( /** Parent class constructor */ _overlay: Overlay, _injector: Injector, @Optional() @Inject(DEFAULT_DIALOG_CONFIG) _defaultOptions: DialogConfig, @Optional() @SkipSelf() _parentDialog: Dialog, _overlayContainer: OverlayContainer, @Inject(DIALOG_SCROLL_STRATEGY) scrollStrategy: any, /** Not in parent class */ @Optional() router: Router, @Optional() authService: AuthService, protected i18nService: I18nService, ) { super(_overlay, _injector, _defaultOptions, _parentDialog, _overlayContainer, scrollStrategy); /** Close all open dialogs if the vault locks */ if (router && authService) { router.events .pipe( filter((event) => event instanceof NavigationEnd), switchMap(() => authService.getAuthStatus()), filter((v) => v !== AuthenticationStatus.Unlocked), takeUntil(this._destroy$), ) .subscribe(() => this.closeAll()); } } override ngOnDestroy(): void { this._destroy$.next(); this._destroy$.complete(); super.ngOnDestroy(); } override open( componentOrTemplateRef: ComponentType | TemplateRef, config?: DialogConfig>, ): DialogRef { config = { backdropClass: this.backDropClasses, ...config, }; return super.open(componentOrTemplateRef, config); } /** * Opens a simple dialog, returns true if the user accepted the dialog. * * @param {SimpleDialogOptions} simpleDialogOptions - An object containing options for the dialog. * @returns `boolean` - True if the user accepted the dialog, false otherwise. */ async openSimpleDialog(simpleDialogOptions: SimpleDialogOptions): Promise { const dialogRef = this.openSimpleDialogRef(simpleDialogOptions); return firstValueFrom(dialogRef.closed); } /** * Opens a simple dialog. * * You should probably use `openSimpleDialog` instead, unless you need to programmatically close the dialog. * * @param {SimpleDialogOptions} simpleDialogOptions - An object containing options for the dialog. * @returns `DialogRef` - The reference to the opened dialog. * Contains a closed observable which can be subscribed to for determining which button * a user pressed */ openSimpleDialogRef(simpleDialogOptions: SimpleDialogOptions): DialogRef { return this.open(SimpleConfigurableDialogComponent, { data: simpleDialogOptions, disableClose: simpleDialogOptions.disableClose, }); } protected translate(translation: string | Translation, defaultKey?: string): string { if (translation == null && defaultKey == null) { return null; } if (translation == null) { return this.i18nService.t(defaultKey); } // Translation interface use implies we must localize. if (typeof translation === "object") { return this.i18nService.t(translation.key, ...(translation.placeholders ?? [])); } return translation; } }