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/backup-drives/backup.service.ts b/web/projects/ui/src/app/components/backup-drives/backup.service.ts index cc1898e7e..7e05178bf 100644 --- a/web/projects/ui/src/app/components/backup-drives/backup.service.ts +++ b/web/projects/ui/src/app/components/backup-drives/backup.service.ts @@ -61,7 +61,7 @@ export class BackupService { ) } - async hasThisBackup(target: BackupTarget, id: string): Promise { + hasThisBackup(target: BackupTarget, id: string): boolean { return ( target.startOs[id] && this.emver.compare(target.startOs[id].version, '0.3.6') !== -1 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/components/form/form-select/form-select.component.html b/web/projects/ui/src/app/components/form/form-select/form-select.component.html index c10e62018..fe2b561c7 100644 --- a/web/projects/ui/src/app/components/form/form-select/form-select.component.html +++ b/web/projects/ui/src/app/components/form/form-select/form-select.component.html @@ -11,6 +11,7 @@ * 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 e1f4f8961..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,23 +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 { - 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 b570b1603..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, @@ -60,7 +61,7 @@ export class ServerBackupPage { component: BackupSelectPage, }) - modal.onWillDismiss().then(res => { + modal.onDidDismiss().then(res => { if (res.data) { this.serviceIds = res.data this.presentModalPassword(target) @@ -74,28 +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) // 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 { @@ -103,41 +111,51 @@ export class ServerBackupPage { } catch { setTimeout( () => this.presentModalOldPassword(target, password), - 500, + 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) => { - const passwordHash = target.entry.startOs[id].passwordHash! - argon2.verify(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!) }) }