From 0f5cec0a604f4d6714e0640a5684abcaa58e9d9c Mon Sep 17 00:00:00 2001 From: waterplea Date: Fri, 12 Jul 2024 11:14:45 +0500 Subject: [PATCH] fix: fix wrong password messaging --- web/package-lock.json | 94 +++++++++---------- web/package.json | 12 +-- .../shared/src/services/error.service.ts | 1 - .../badge-menu-button/badge-menu.component.ts | 4 +- .../backup-server-select.page.ts | 44 ++++++--- ....modal.ts => password-prompt.component.ts} | 45 +++++++-- .../server-backup/server-backup.page.scss | 0 .../server-backup/server-backup.page.ts | 81 +++++++++------- .../ui/src/app/pages/widgets/widgets.page.ts | 4 +- 9 files changed, 170 insertions(+), 115 deletions(-) rename web/projects/ui/src/app/modals/{backup-server-select/password-prompt.modal.ts => password-prompt.component.ts} (59%) delete mode 100644 web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.scss diff --git a/web/package-lock.json b/web/package-lock.json index e07531a96..cf32bbc01 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -28,12 +28,12 @@ "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", "@start9labs/start-sdk": "file:../sdk/dist", - "@taiga-ui/addon-charts": "3.84.0", - "@taiga-ui/cdk": "3.84.0", - "@taiga-ui/core": "3.84.0", - "@taiga-ui/experimental": "3.84.0", - "@taiga-ui/icons": "3.84.0", - "@taiga-ui/kit": "3.84.0", + "@taiga-ui/addon-charts": "3.86.0", + "@taiga-ui/cdk": "3.86.0", + "@taiga-ui/core": "3.86.0", + "@taiga-ui/experimental": "3.86.0", + "@taiga-ui/icons": "3.86.0", + "@taiga-ui/kit": "3.86.0", "@tinkoff/ng-dompurify": "4.0.0", "@tinkoff/ng-event-plugins": "3.2.0", "angular-svg-round-progressbar": "^9.0.0", @@ -4128,9 +4128,9 @@ } }, "node_modules/@taiga-ui/addon-charts": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.84.0.tgz", - "integrity": "sha512-XR7UFywnrv4NRLHOCbba63gXDYYDL4Rt0MbjnF54p5U2EXnbt2of7VbjlB6cPx40XkQqfqa3CNayYxWZP82Ijg==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.86.0.tgz", + "integrity": "sha512-Du/85qqaj8hpFSI6hPuFeIhtE93Z6WSkYZLt0gvnsaCb2qSAg8D4oHSogrtF1rsWGGoM+fvXjD7UEUw9GzFIPg==", "dependencies": { "tslib": "^2.6.2" }, @@ -4138,15 +4138,15 @@ "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", "@ng-web-apis/common": "^3.0.6", - "@taiga-ui/cdk": "^3.84.0", - "@taiga-ui/core": "^3.84.0", + "@taiga-ui/cdk": "^3.86.0", + "@taiga-ui/core": "^3.86.0", "@tinkoff/ng-polymorpheus": "^4.3.0" } }, "node_modules/@taiga-ui/addon-commerce": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-3.84.0.tgz", - "integrity": "sha512-1zqLwnZLAYYcHvjH89d7JmtV2+QeZ2YnSJ3YWEMNLjGPzpev4RvQXtDfglIyu0LCyTxqpXmuzes9v/cgq2P5TQ==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-3.86.0.tgz", + "integrity": "sha512-8QSB490ckI4jnU+1sQ3x8os2GVE162hbvzPVYIZ0TruoeXl076dAz6PT2WRaFwjcaCAIGsuaQgQ4Cv02NjkiYQ==", "peer": true, "dependencies": { "tslib": "^2.6.2" @@ -4159,18 +4159,18 @@ "@maskito/core": "^1.9.0", "@maskito/kit": "^1.9.0", "@ng-web-apis/common": "^3.0.6", - "@taiga-ui/cdk": "^3.84.0", - "@taiga-ui/core": "^3.84.0", - "@taiga-ui/i18n": "^3.84.0", - "@taiga-ui/kit": "^3.84.0", + "@taiga-ui/cdk": "^3.86.0", + "@taiga-ui/core": "^3.86.0", + "@taiga-ui/i18n": "^3.86.0", + "@taiga-ui/kit": "^3.86.0", "@tinkoff/ng-polymorpheus": "^4.3.0", "rxjs": ">=6.0.0" } }, "node_modules/@taiga-ui/cdk": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.84.0.tgz", - "integrity": "sha512-0umw/CUmYNEYOCUNQVTQS53zXzxZsH/6+lj1mFVzocvfJFJWAUT6ltCH9QvxYmxSDDGWwNGg16AaVo2K+aGL0w==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.86.0.tgz", + "integrity": "sha512-aVbnW01Oh0Er1sHKVGHP8W05mOSKxjSzFE3Qx4iF4T6KW7Rlz9HZoNx5ADMg0TATYChtWh9Kwjo8I4LSVj2ZUw==", "dependencies": { "@ng-web-apis/common": "3.0.6", "@ng-web-apis/mutation-observer": "3.1.0", @@ -4221,11 +4221,11 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@taiga-ui/core": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.84.0.tgz", - "integrity": "sha512-FZy77z0E4qjYcszVcp+qPFkPwJPl8qXZb7t2P+juUtJvSmSn2foQHHdyhbIYN808H26tqCdgkTMG1BWQxVuDSg==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.86.0.tgz", + "integrity": "sha512-diQKOnPtDDfxPOMk6wLRq8nyDVfNSPSNy+1TeyqzUgOvJ6XAjfaBXGsL3iuR7AN8+sz/b3rJmBce+vdw6FjMLQ==", "dependencies": { - "@taiga-ui/i18n": "^3.84.0", + "@taiga-ui/i18n": "^3.86.0", "tslib": "^2.6.2" }, "peerDependencies": { @@ -4237,35 +4237,35 @@ "@angular/router": ">=12.0.0", "@ng-web-apis/common": "^3.0.6", "@ng-web-apis/mutation-observer": "^3.1.0", - "@taiga-ui/cdk": "^3.84.0", - "@taiga-ui/i18n": "^3.84.0", + "@taiga-ui/cdk": "^3.86.0", + "@taiga-ui/i18n": "^3.86.0", "@tinkoff/ng-event-plugins": "^3.2.0", "@tinkoff/ng-polymorpheus": "^4.3.0", "rxjs": ">=6.0.0" } }, "node_modules/@taiga-ui/experimental": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-3.84.0.tgz", - "integrity": "sha512-q0hNVy+EmywCG8hpZlg/+haKIFhnmxicQiSeV/D1P7CHO10safjGo0ptT6e1hYMFa5/cJZOM4OwDPen2xs17Wg==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-3.86.0.tgz", + "integrity": "sha512-ACjoRVeX5MgsNJsiu2ukliXLD2mfEWm8Vtmk78vqcnkyPUmy1ZWK4sG3p5ybFN8AdIMHkblVq0l+x2qAwr/+LQ==", "dependencies": { "tslib": "^2.6.2" }, "peerDependencies": { "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", - "@taiga-ui/addon-commerce": "^3.84.0", - "@taiga-ui/cdk": "^3.84.0", - "@taiga-ui/core": "^3.84.0", - "@taiga-ui/kit": "^3.84.0", + "@taiga-ui/addon-commerce": "^3.86.0", + "@taiga-ui/cdk": "^3.86.0", + "@taiga-ui/core": "^3.86.0", + "@taiga-ui/kit": "^3.86.0", "@tinkoff/ng-polymorpheus": "^4.3.0", "rxjs": ">=6.0.0" } }, "node_modules/@taiga-ui/i18n": { - "version": "3.85.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.85.0.tgz", - "integrity": "sha512-CGoxfq9WY+psX5ZOfWmuQZ6OA/0CAPYJTlbHkw5sRKAyhEQ3NM/Wbx3xcwrcYRRJDnt9yOlfibz+3a+WDF2bFA==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.86.0.tgz", + "integrity": "sha512-8zkNhMo/QtxZ2Zp6EP/nxo4SOLwaIrX+P3X/Wt+1cjFNZUYWWfdvfHLLdNviKFPVl4RAOxvkhDfza/wkrwv+iQ==", "dependencies": { "tslib": "^2.6.2" }, @@ -4276,20 +4276,20 @@ } }, "node_modules/@taiga-ui/icons": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.84.0.tgz", - "integrity": "sha512-KiH7BJRZ6wbkOHlJAS0XHq2gYnQTpRgdEogKW+GoD0da/4trCdM66vhDk2j0DwDFdBGq5U0inHJCjnskBI1nSQ==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.86.0.tgz", + "integrity": "sha512-jVBEbvE/r9JG+knmXMTn/l/js3JjYi8nSGbrLCryJZZoS2izRnQARN2txABieUJm8H463CoF0rcdXlHKRuA4Ew==", "dependencies": { "tslib": "^2.6.2" }, "peerDependencies": { - "@taiga-ui/cdk": "^3.84.0" + "@taiga-ui/cdk": "^3.86.0" } }, "node_modules/@taiga-ui/kit": { - "version": "3.84.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.84.0.tgz", - "integrity": "sha512-lSUPDco5FeBYK3ESnXeEPLCdMCmNXwcdHNK/we+0ZoH4VPx/OGg2hpEP0Fej7jfGHwXFTzDbufQD0hT6WlfTAw==", + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.86.0.tgz", + "integrity": "sha512-naAy4pyhCaQ9+vWxqSMjbV+9KwnMxT5ybrw+MAJgMn2evzRq0FjqzyFZFog7oiRbRvgVdoWPQfBNKaaLhJcpsw==", "dependencies": { "@maskito/angular": "1.9.0", "@maskito/core": "1.9.0", @@ -4306,9 +4306,9 @@ "@ng-web-apis/common": "3.0.6", "@ng-web-apis/mutation-observer": "^3.1.0", "@ng-web-apis/resize-observer": "^3.0.6", - "@taiga-ui/cdk": "^3.84.0", - "@taiga-ui/core": "^3.84.0", - "@taiga-ui/i18n": "^3.84.0", + "@taiga-ui/cdk": "^3.86.0", + "@taiga-ui/core": "^3.86.0", + "@taiga-ui/i18n": "^3.86.0", "@tinkoff/ng-polymorpheus": "^4.3.0", "rxjs": ">=6.0.0" } diff --git a/web/package.json b/web/package.json index 69c3585ce..a75701333 100644 --- a/web/package.json +++ b/web/package.json @@ -51,12 +51,12 @@ "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", "@start9labs/start-sdk": "file:../sdk/dist", - "@taiga-ui/addon-charts": "3.84.0", - "@taiga-ui/cdk": "3.84.0", - "@taiga-ui/core": "3.84.0", - "@taiga-ui/experimental": "3.84.0", - "@taiga-ui/icons": "3.84.0", - "@taiga-ui/kit": "3.84.0", + "@taiga-ui/addon-charts": "3.86.0", + "@taiga-ui/cdk": "3.86.0", + "@taiga-ui/core": "3.86.0", + "@taiga-ui/experimental": "3.86.0", + "@taiga-ui/icons": "3.86.0", + "@taiga-ui/kit": "3.86.0", "@tinkoff/ng-dompurify": "4.0.0", "@tinkoff/ng-event-plugins": "3.2.0", "angular-svg-round-progressbar": "^9.0.0", diff --git a/web/projects/shared/src/services/error.service.ts b/web/projects/shared/src/services/error.service.ts index 45891e0f4..383b3fc97 100644 --- a/web/projects/shared/src/services/error.service.ts +++ b/web/projects/shared/src/services/error.service.ts @@ -15,7 +15,6 @@ export class ErrorService extends ErrorHandler { this.alerts .open(getErrorMessage(error, link), { label: 'Error', - autoClose: false, status: TuiNotification.Error, }) .subscribe() diff --git a/web/projects/ui/src/app/components/badge-menu-button/badge-menu.component.ts b/web/projects/ui/src/app/components/badge-menu-button/badge-menu.component.ts index 5fe9b621e..0d7f3ec66 100644 --- a/web/projects/ui/src/app/components/badge-menu-button/badge-menu.component.ts +++ b/web/projects/ui/src/app/components/badge-menu-button/badge-menu.component.ts @@ -32,7 +32,7 @@ export class BadgeMenuComponent { constructor( private readonly splitPane: SplitPaneTracker, private readonly patch: PatchDB, - private readonly dialog: TuiDialogService, + private readonly dialogs: TuiDialogService, private readonly clientStorageService: ClientStorageService, ) {} @@ -44,6 +44,6 @@ export class BadgeMenuComponent { } onWidgets() { - this.dialog.open(WIDGETS_COMPONENT, { label: 'Widgets' }).subscribe() + this.dialogs.open(WIDGETS_COMPONENT, { label: 'Widgets' }).subscribe() } } diff --git a/web/projects/ui/src/app/modals/backup-server-select/backup-server-select.page.ts b/web/projects/ui/src/app/modals/backup-server-select/backup-server-select.page.ts index 0f1c47276..a10067044 100644 --- a/web/projects/ui/src/app/modals/backup-server-select/backup-server-select.page.ts +++ b/web/projects/ui/src/app/modals/backup-server-select/backup-server-select.page.ts @@ -6,6 +6,10 @@ import { LoadingService, StartOSDiskInfo, } from '@start9labs/shared' +import { + PasswordPromptComponent, + PromptOptions, +} from 'src/app/modals/password-prompt.component' import { BackupInfo, CifsBackupTarget, @@ -14,7 +18,6 @@ import { import { ApiService } from 'src/app/services/api/embassy-api.service' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' import { AppRecoverSelectPage } from '../app-recover-select/app-recover-select.page' -import { PasswordPromptModal } from './password-prompt.modal' @Component({ selector: 'backup-server-select', @@ -38,24 +41,35 @@ export class BackupServerSelectModal { async presentModalPassword( serverId: string, - server: StartOSDiskInfo, + { passwordHash }: StartOSDiskInfo, ): Promise { + const options: PromptOptions = { + title: 'Password Required', + message: + 'Enter the password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.', + label: 'Decrypt Backup', + placeholder: 'Enter password', + buttonText: 'Next', + } const modal = await this.modalCtrl.create({ - component: PasswordPromptModal, + component: PasswordPromptComponent, + componentProps: { options }, + canDismiss: async password => { + if (password === null) { + return true + } + + try { + argon2.verify(passwordHash!, password) + await this.restoreFromBackup(serverId, password) + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } + }, }) modal.present() - - const { data, role } = await modal.onWillDismiss() - - if (role === 'confirm') { - try { - // @TODO Alex if invalid password, we should tell the user "Invalid password" and halt execution of this function. The modal should remain so the user can try again. Correct password is asdfasdf - argon2.verify(server.passwordHash!, data) - await this.restoreFromBackup(serverId, data) - } catch (e: any) { - this.errorService.handleError(e) - } - } } private async restoreFromBackup( diff --git a/web/projects/ui/src/app/modals/backup-server-select/password-prompt.modal.ts b/web/projects/ui/src/app/modals/password-prompt.component.ts similarity index 59% rename from web/projects/ui/src/app/modals/backup-server-select/password-prompt.modal.ts rename to web/projects/ui/src/app/modals/password-prompt.component.ts index 2af89e3d0..a9fec75cd 100644 --- a/web/projects/ui/src/app/modals/backup-server-select/password-prompt.modal.ts +++ b/web/projects/ui/src/app/modals/password-prompt.component.ts @@ -1,14 +1,29 @@ -import { Component } from '@angular/core' +import { + AfterViewInit, + Component, + ElementRef, + Input, + ViewChild, +} from '@angular/core' import { FormsModule } from '@angular/forms' import { IonicModule, ModalController } from '@ionic/angular' +import { TuiTextfieldComponent } from '@taiga-ui/core' import { TuiInputPasswordModule } from '@taiga-ui/kit' +export interface PromptOptions { + title: string + message: string + label: string + placeholder: string + buttonText: string +} + @Component({ standalone: true, template: ` - Decrypt Backup + {{ options.title }} @@ -18,13 +33,11 @@ import { TuiInputPasswordModule } from '@taiga-ui/kit' +

{{ options.message }}

- Enter the password that was used to encrypt this backup. On the next - screen, you will select the individual services you want to restore. -

-

- - Enter password + + {{ options.label }} +

@@ -47,18 +60,30 @@ import { TuiInputPasswordModule } from '@taiga-ui/kit' [disabled]="!password" (click)="confirm()" > - Next + {{ options.buttonText }} `, imports: [IonicModule, FormsModule, TuiInputPasswordModule], }) -export class PasswordPromptModal { +export class PasswordPromptComponent implements AfterViewInit { + @ViewChild(TuiTextfieldComponent, { read: ElementRef }) + input?: ElementRef + + @Input() + options!: PromptOptions + password = '' constructor(private modalCtrl: ModalController) {} + ngAfterViewInit() { + setTimeout(() => { + this.input?.nativeElement.focus({ preventScroll: true }) + }, 300) + } + cancel() { return this.modalCtrl.dismiss(null, 'cancel') } diff --git a/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.scss b/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts b/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts index 25532c256..9a067bc1e 100644 --- a/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts @@ -1,11 +1,13 @@ import { Component } from '@angular/core' import { ModalController, NavController } from '@ionic/angular' -import { LoadingService } from '@start9labs/shared' -import { TuiDialogService } from '@taiga-ui/core' -import { PROMPT, PromptOptions } from 'src/app/modals/prompt.component' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { + PasswordPromptComponent, + PromptOptions, +} from 'src/app/modals/password-prompt.component' import { ApiService } from 'src/app/services/api/embassy-api.service' import { PatchDB } from 'patch-db-client' -import { skip, take, takeUntil } from 'rxjs/operators' +import { skip, takeUntil } from 'rxjs/operators' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' import * as argon2 from '@start9labs/argon2' import { TuiDestroyService } from '@taiga-ui/cdk' @@ -22,7 +24,6 @@ import { BackupService } from 'src/app/components/backup-drives/backup.service' @Component({ selector: 'server-backup', templateUrl: './server-backup.page.html', - styleUrls: ['./server-backup.page.scss'], providers: [TuiDestroyService], }) export class ServerBackupPage { @@ -31,8 +32,8 @@ export class ServerBackupPage { readonly backingUp$ = this.eosService.backingUp$ constructor( + private readonly errorService: ErrorService, private readonly loader: LoadingService, - private readonly dialogs: TuiDialogService, private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, private readonly navCtrl: NavController, @@ -74,29 +75,35 @@ export class ServerBackupPage { target: MappedBackupTarget, ): Promise { const options: PromptOptions = { + title: 'Master Password Needed', message: 'Enter your master password to encrypt this backup.', label: 'Master Password', placeholder: 'Enter master password', - useMask: true, buttonText: 'Create Backup', } - this.dialogs - .open(PROMPT, { - label: 'Master Password Needed', - data: options, - }) - .pipe(take(1)) - .subscribe(async (password: string) => { + const modal = await this.modalCtrl.create({ + component: PasswordPromptComponent, + componentProps: { options }, + canDismiss: async password => { + if (password === null) { + return true + } + const { passwordHash, id } = await getServerInfo(this.patch) - // @TODO Alex if invalid password, we should tell the user "Invalid password" and halt execution of this function. The modal should remain so the user can try again. Correct password is asdfasdf // confirm password matches current master password - argon2.verify(passwordHash, password) + try { + argon2.verify(passwordHash, password) + } catch (e: any) { + this.errorService.handleError(e) + return false + } // first time backup if (!this.backupService.hasThisBackup(target.entry, id)) { - await this.createBackup(target, password) + this.createBackup(target, password) + return true // existing backup } else { try { @@ -106,39 +113,49 @@ export class ServerBackupPage { () => this.presentModalOldPassword(target, password), 250, ) - return + return true } await this.createBackup(target, password) + return true } - }) + }, + }) + modal.present() } private async presentModalOldPassword( target: MappedBackupTarget, password: string, ): Promise { + const { id } = await getServerInfo(this.patch) const options: PromptOptions = { + title: 'Original Password Needed', message: 'This backup was created with a different password. Enter the ORIGINAL password that was used to encrypt this backup.', label: 'Original Password', placeholder: 'Enter original password', - useMask: true, buttonText: 'Create Backup', } - const { id } = await getServerInfo(this.patch) + const modal = await this.modalCtrl.create({ + component: PasswordPromptComponent, + componentProps: { options }, + canDismiss: async oldPassword => { + if (oldPassword === null) { + return true + } - this.dialogs - .open(PROMPT, { - label: 'Original Password Needed', - data: options, - }) - .pipe(take(1)) - .subscribe(async (oldPassword: string) => { - // @TODO Alex if invalid password, we should tell the user "Invalid password" and halt execution of this function. The modal should remain so the user can try again. Correct password is asdfasdf - argon2.verify(target.entry.startOs[id].passwordHash!, oldPassword) - await this.createBackup(target, password, oldPassword) - }) + try { + argon2.verify(target.entry.startOs[id].passwordHash!, oldPassword) + await this.createBackup(target, password, oldPassword) + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } + }, + }) + modal.present() } private async createBackup( diff --git a/web/projects/ui/src/app/pages/widgets/widgets.page.ts b/web/projects/ui/src/app/pages/widgets/widgets.page.ts index ca1541269..30d605bda 100644 --- a/web/projects/ui/src/app/pages/widgets/widgets.page.ts +++ b/web/projects/ui/src/app/pages/widgets/widgets.page.ts @@ -56,7 +56,7 @@ export class WidgetsPage { @Optional() @Inject(POLYMORPHEUS_CONTEXT) readonly context: TuiDialogContext | null, - private readonly dialog: TuiDialogService, + private readonly dialogs: TuiDialogService, private readonly patch: PatchDB, private readonly cdr: ChangeDetectorRef, private readonly api: ApiService, @@ -83,7 +83,7 @@ export class WidgetsPage { } add() { - this.dialog.open(ADD_WIDGET, { label: 'Add widget' }).subscribe(widget => { + this.dialogs.open(ADD_WIDGET, { label: 'Add widget' }).subscribe(widget => { this.addWidget(widget!) }) }