From a31ab53f217cb226facbe40521f3d068a7deaee3 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 31 Jan 2019 20:51:11 +0100 Subject: [PATCH] Base components. --- .../event-consumers-page.component.ts | 21 ++---- .../pages/restore/restore-page.component.ts | 28 ++++---- .../pages/users/user-page.component.ts | 18 ++--- .../pages/content/content-page.component.ts | 54 +++++++-------- .../contents-filters-page.component.ts | 17 ++--- .../pages/contents/contents-page.component.ts | 29 +++------ .../shared/array-editor.component.html | 2 +- .../content/shared/array-editor.component.ts | 21 ++++-- .../shared/assets-editor.component.html | 10 +-- .../content/shared/assets-editor.component.ts | 36 +++++----- .../shared/preview-button.component.html | 10 +-- .../shared/preview-button.component.ts | 39 +++++++---- .../shared/references-editor.component.ts | 4 +- .../pages/dashboard-page.component.ts | 27 +++----- .../pages/schema/schema-page.component.ts | 25 ++++--- .../pages/schema/types/number-ui.component.ts | 29 ++++----- .../pages/schema/types/string-ui.component.ts | 28 ++++---- .../types/string-validation.component.ts | 16 ++--- .../pages/schemas/schemas-page.component.ts | 32 ++++----- .../pages/backups/backups-page.component.ts | 24 +++---- .../contributors-page.component.ts | 4 +- .../languages/languages-page.component.ts | 21 +++--- .../angular/forms/autocomplete.component.ts | 5 +- .../angular/forms/code-editor.component.ts | 3 +- .../angular/forms/control-errors.component.ts | 4 +- .../forms/date-time-editor.component.ts | 4 +- .../angular/forms/iframe-editor.component.ts | 2 +- .../angular/forms/json-editor.component.ts | 2 + .../angular/forms/stars.component.ts | 1 + .../angular/forms/tag-editor.component.html | 2 +- .../angular/forms/tag-editor.component.ts | 3 +- .../angular/forms/toggle.component.ts | 4 +- .../angular/ignore-scrollbar.directive.ts | 25 +++---- .../angular/image-source.directive.ts | 19 +++--- .../modals/dialog-renderer.component.ts | 13 ++-- .../modals/modal-dialog.component.html | 4 +- .../angular/modals/modal-dialog.component.ts | 27 ++++---- .../angular/modals/modal-target.directive.ts | 36 ++++------ .../angular/modals/modal-view.directive.ts | 37 +++-------- .../modals/onboarding-tooltip.component.ts | 10 +-- .../angular/modals/tooltip.component.ts | 4 +- .../angular/routers/parent-link.directive.ts | 17 ++--- .../angular/shortcut.component.spec.ts | 18 +++-- .../app/framework/angular/sorted.directive.ts | 4 +- .../framework/angular/stateful.component.ts | 53 ++++++++++----- .../angular/user-report.component.ts | 8 +-- .../app/framework/services/loading.service.ts | 4 +- src/Squidex/app/framework/state.ts | 14 ++-- .../shared/components/app-form.component.ts | 3 +- .../shared/components/asset.component.html | 28 ++++---- .../app/shared/components/asset.component.ts | 50 +++++++------- .../components/assets-selector.component.html | 18 ++--- .../components/assets-selector.component.ts | 60 ++++++++++------- .../shared/components/comments.component.ts | 19 +++--- .../geolocation-editor.component.ts | 1 + .../components/markdown-editor.component.ts | 4 +- .../shared/components/permission.directive.ts | 44 +++++++++++-- .../components/rich-editor.component.ts | 8 ++- .../components/schema-category.component.html | 12 ++-- .../components/schema-category.component.scss | 8 ++- .../components/schema-category.component.ts | 65 ++++++++++++------- src/Squidex/app/shared/state/apps.state.ts | 2 +- src/Squidex/app/shared/state/schemas.state.ts | 17 +++-- .../pages/internal/internal-area.component.ts | 19 ++---- .../internal/profile-menu.component.html | 6 +- .../pages/internal/profile-menu.component.ts | 47 +++++++------- 66 files changed, 618 insertions(+), 611 deletions(-) diff --git a/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts b/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts index d7c4b44ce..ffde74647 100644 --- a/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts +++ b/src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts @@ -5,11 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Subscription, timer } from 'rxjs'; +import { Component, OnInit } from '@angular/core'; +import { timer } from 'rxjs'; import { onErrorResumeNext, switchMap } from 'rxjs/operators'; -import { DialogModel } from '@app/shared'; +import { DialogModel, ResourceOwner } from '@app/shared'; import { EventConsumerDto } from './../../services/event-consumers.service'; import { EventConsumersState } from './../../state/event-consumers.state'; @@ -19,27 +19,20 @@ import { EventConsumersState } from './../../state/event-consumers.state'; styleUrls: ['./event-consumers-page.component.scss'], templateUrl: './event-consumers-page.component.html' }) -export class EventConsumersPageComponent implements OnDestroy, OnInit { - private timerSubscription: Subscription; - +export class EventConsumersPageComponent extends ResourceOwner implements OnInit { public eventConsumerErrorDialog = new DialogModel(); public eventConsumerError = ''; constructor( public readonly eventConsumersState: EventConsumersState ) { - } - - public ngOnDestroy() { - this.timerSubscription.unsubscribe(); + super(); } public ngOnInit() { this.eventConsumersState.load().pipe(onErrorResumeNext()).subscribe(); - this.timerSubscription = - timer(2000, 2000).pipe(switchMap(x => this.eventConsumersState.load(true, true).pipe(onErrorResumeNext()))) - .subscribe(); + this.takeOver(timer(2000, 2000).pipe(switchMap(() => this.eventConsumersState.load(true, true)))); } public reload() { @@ -58,7 +51,7 @@ export class EventConsumersPageComponent implements OnDestroy, OnInit { this.eventConsumersState.reset(es).pipe(onErrorResumeNext()).subscribe(); } - public trackByEventConsumer(index: number, es: EventConsumerDto) { + public trackByEventConsumer(es: EventConsumerDto) { return es.name; } diff --git a/src/Squidex/app/features/administration/pages/restore/restore-page.component.ts b/src/Squidex/app/features/administration/pages/restore/restore-page.component.ts index 026f0becb..27c25e171 100644 --- a/src/Squidex/app/features/administration/pages/restore/restore-page.component.ts +++ b/src/Squidex/app/features/administration/pages/restore/restore-page.component.ts @@ -5,15 +5,16 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; -import { Subscription, timer } from 'rxjs'; -import { filter, onErrorResumeNext, switchMap } from 'rxjs/operators'; +import { timer } from 'rxjs'; +import { onErrorResumeNext, switchMap } from 'rxjs/operators'; import { AuthService, BackupsService, DialogService, + ResourceOwner, RestoreDto, RestoreForm } from '@app/shared'; @@ -23,9 +24,7 @@ import { styleUrls: ['./restore-page.component.scss'], templateUrl: './restore-page.component.html' }) -export class RestorePageComponent implements OnDestroy, OnInit { - private timerSubscription: Subscription; - +export class RestorePageComponent extends ResourceOwner implements OnInit { public restoreJob: RestoreDto | null; public restoreForm = new RestoreForm(this.formBuilder); @@ -35,18 +34,17 @@ export class RestorePageComponent implements OnDestroy, OnInit { private readonly dialogs: DialogService, private readonly formBuilder: FormBuilder ) { - } - - public ngOnDestroy() { - this.timerSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.timerSubscription = - timer(0, 2000).pipe(switchMap(() => this.backupsService.getRestore().pipe(onErrorResumeNext())), filter(x => !!x)) - .subscribe(dto => { - this.restoreJob = dto!; - }); + this.takeOver( + timer(0, 2000).pipe(switchMap(() => this.backupsService.getRestore().pipe(onErrorResumeNext()))) + .subscribe(job => { + if (job) { + this.restoreJob = job; + } + })); } public restore() { diff --git a/src/Squidex/app/features/administration/pages/users/user-page.component.ts b/src/Squidex/app/features/administration/pages/users/user-page.component.ts index 27d10c5ce..59750b200 100644 --- a/src/Squidex/app/features/administration/pages/users/user-page.component.ts +++ b/src/Squidex/app/features/administration/pages/users/user-page.component.ts @@ -5,10 +5,11 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; + +import { ResourceOwner } from '@app/shared'; import { UserDto } from './../../services/users.service'; import { UserForm, UsersState } from './../../state/users.state'; @@ -18,9 +19,7 @@ import { UserForm, UsersState } from './../../state/users.state'; styleUrls: ['./user-page.component.scss'], templateUrl: './user-page.component.html' }) -export class UserPageComponent implements OnDestroy, OnInit { - private selectedUserSubscription: Subscription; - +export class UserPageComponent extends ResourceOwner implements OnInit { public canUpdate = false; public user?: { user: UserDto, isCurrentUser: boolean }; @@ -32,14 +31,11 @@ export class UserPageComponent implements OnDestroy, OnInit { private readonly route: ActivatedRoute, private readonly router: Router ) { - } - - public ngOnDestroy() { - this.selectedUserSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.selectedUserSubscription = + this.takeOver( this.usersState.selectedUser .subscribe(selectedUser => { this.user = selectedUser!; @@ -47,7 +43,7 @@ export class UserPageComponent implements OnDestroy, OnInit { if (selectedUser) { this.userForm.load(selectedUser.user); } - }); + })); } public save() { diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.ts b/src/Squidex/app/features/content/pages/content/content-page.component.ts index a880fa5c2..6508507e2 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-page.component.ts @@ -5,10 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable, of, Subscription } from 'rxjs'; -import { filter, onErrorResumeNext, switchMap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { onErrorResumeNext, switchMap } from 'rxjs/operators'; import { ContentVersionSelected } from './../messages'; @@ -25,6 +25,7 @@ import { LanguagesState, MessageBus, ModalModel, + ResourceOwner, SchemaDetailsDto, SchemasState, Version @@ -40,12 +41,7 @@ import { DueTimeSelectorComponent } from './../../shared/due-time-selector.compo fadeAnimation ] }) -export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, OnInit { - private languagesSubscription: Subscription; - private contentSubscription: Subscription; - private contentVersionSelectedSubscription: Subscription; - private selectedSchemaSubscription: Subscription; - +export class ContentPageComponent extends ResourceOwner implements CanComponentDeactivate, OnInit { public schema: SchemaDetailsDto; public content: ContentDto; @@ -70,44 +66,42 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, private readonly router: Router, private readonly schemasState: SchemasState ) { - } - - public ngOnDestroy() { - this.languagesSubscription.unsubscribe(); - this.contentSubscription.unsubscribe(); - this.contentVersionSelectedSubscription.unsubscribe(); - this.selectedSchemaSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.languagesSubscription = + this.takeOver( this.languagesState.languages .subscribe(languages => { this.languages = languages.map(x => x.language); this.language = this.languages.at(0); - }); + })); - this.selectedSchemaSubscription = - this.schemasState.selectedSchema.pipe(filter(s => !!s)) + this.takeOver( + this.schemasState.selectedSchema .subscribe(schema => { - this.schema = schema!; + if (schema) { + this.schema = schema!; - this.contentForm = new EditContentForm(this.schema, this.languages); - }); + this.contentForm = new EditContentForm(this.schema, this.languages); + } + })); - this.contentSubscription = - this.contentsState.selectedContent.pipe(filter(c => !!c)) + this.takeOver( + this.contentsState.selectedContent .subscribe(content => { - this.content = content!; + if (content) { + this.content = content; - this.loadContent(this.content.dataDraft); - }); + this.loadContent(this.content.dataDraft); + } + })); - this.contentVersionSelectedSubscription = + this.takeOver( this.messageBus.of(ContentVersionSelected) .subscribe(message => { this.loadVersion(message.version); - }); + })); } public canDeactivate(): Observable { diff --git a/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts b/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts index 9701956cc..26f45801d 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts +++ b/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts @@ -5,13 +5,13 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Component, OnInit } from '@angular/core'; import { onErrorResumeNext } from 'rxjs/operators'; import { ContentsState, Queries, + ResourceOwner, SchemasState, UIState } from '@app/shared'; @@ -21,9 +21,7 @@ import { styleUrls: ['./contents-filters-page.component.scss'], templateUrl: './contents-filters-page.component.html' }) -export class ContentsFiltersPageComponent implements OnDestroy, OnInit { - private selectedSchemaSubscription: Subscription; - +export class ContentsFiltersPageComponent extends ResourceOwner implements OnInit { public schemaQueries: Queries; constructor( @@ -31,20 +29,17 @@ export class ContentsFiltersPageComponent implements OnDestroy, OnInit { private readonly schemasState: SchemasState, private readonly uiState: UIState ) { - } - - public ngOnDestroy() { - this.selectedSchemaSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.selectedSchemaSubscription = + this.takeOver( this.schemasState.selectedSchema .subscribe(schema => { if (schema) { this.schemaQueries = new Queries(this.uiState, `schemas.${schema.name}`); } - }); + })); } public search(query: string) { diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts index d0f79bd98..3bbca2169 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts @@ -5,8 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { onErrorResumeNext, switchMap, tap } from 'rxjs/operators'; import { @@ -18,6 +17,7 @@ import { LanguagesState, ModalModel, Queries, + ResourceOwner, SchemaDetailsDto, SchemasState, UIState @@ -30,11 +30,7 @@ import { DueTimeSelectorComponent } from './../../shared/due-time-selector.compo styleUrls: ['./contents-page.component.scss'], templateUrl: './contents-page.component.html' }) -export class ContentsPageComponent implements OnDestroy, OnInit { - private contentsSubscription: Subscription; - private languagesSubscription: Subscription; - private selectedSchemaSubscription: Subscription; - +export class ContentsPageComponent extends ResourceOwner implements OnInit { public schema: SchemaDetailsDto; public schemaQueries: Queries; @@ -61,16 +57,11 @@ export class ContentsPageComponent implements OnDestroy, OnInit { private readonly schemasState: SchemasState, private readonly uiState: UIState ) { - } - - public ngOnDestroy() { - this.contentsSubscription.unsubscribe(); - this.languagesSubscription.unsubscribe(); - this.selectedSchemaSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.selectedSchemaSubscription = + this.takeOver( this.schemasState.selectedSchema .subscribe(schema => { this.resetSelection(); @@ -79,20 +70,20 @@ export class ContentsPageComponent implements OnDestroy, OnInit { this.schemaQueries = new Queries(this.uiState, `schemas.${this.schema.name}`); this.contentsState.init().pipe(onErrorResumeNext()).subscribe(); - }); + })); - this.contentsSubscription = + this.takeOver( this.contentsState.contents .subscribe(() => { this.updateSelectionSummary(); - }); + })); - this.languagesSubscription = + this.takeOver( this.languagesState.languages .subscribe(languages => { this.languages = languages.map(x => x.language); this.language = this.languages.at(0); - }); + })); } public reload() { diff --git a/src/Squidex/app/features/content/shared/array-editor.component.html b/src/Squidex/app/features/content/shared/array-editor.component.html index 81f476dfc..449225ce4 100644 --- a/src/Squidex/app/features/content/shared/array-editor.component.html +++ b/src/Squidex/app/features/content/shared/array-editor.component.html @@ -5,7 +5,7 @@ { @Input() public form: EditContentForm; @@ -37,10 +42,14 @@ export class ArrayEditorComponent { @Input() public arrayControl: FormArray; - public isHidden = false; + constructor(changeDetector: ChangeDetectorRef) { + super(changeDetector, { + isHidden: false + }); + } - public hide(hide: boolean) { - this.isHidden = hide; + public hide(isHidden: boolean) { + this.next(s => ({ ...s, isHidden })); } public removeItem(index: number) { diff --git a/src/Squidex/app/features/content/shared/assets-editor.component.html b/src/Squidex/app/features/content/shared/assets-editor.component.html index 7d2eb53e1..10412407d 100644 --- a/src/Squidex/app/features/content/shared/assets-editor.component.html +++ b/src/Squidex/app/features/content/shared/assets-editor.component.html @@ -22,10 +22,10 @@
- -
@@ -33,14 +33,14 @@
-
-
+
diff --git a/src/Squidex/app/features/content/shared/assets-editor.component.ts b/src/Squidex/app/features/content/shared/assets-editor.component.ts index 3eb4eff53..642805e90 100644 --- a/src/Squidex/app/features/content/shared/assets-editor.component.ts +++ b/src/Squidex/app/features/content/shared/assets-editor.component.ts @@ -35,9 +35,9 @@ class AssetUpdated { } interface State { - newAssets: ImmutableArray; + assetFiles: ImmutableArray; - oldAssets: ImmutableArray; + assets: ImmutableArray; isListView: boolean; } @@ -59,22 +59,22 @@ export class AssetsEditorComponent extends StatefulControlComponent x.id).values)) { + if (!Types.isEquals(obj, this.snapshot.assets.map(x => x.id).values)) { const assetIds: string[] = obj; this.assetsService.getAssets(this.appsState.appName, 0, 0, undefined, undefined, obj) .subscribe(dtos => { this.setAssets(ImmutableArray.of(assetIds.map(id => dtos.items.find(x => x.id === id)!).filter(a => !!a))); - if (this.snapshot.oldAssets.length !== assetIds.length) { + if (this.snapshot.assets.length !== assetIds.length) { this.updateValue(); } }, () => { @@ -91,17 +91,17 @@ export class AssetsEditorComponent extends StatefulControlComponent { if (event.source !== this) { - this.setAssets(this.snapshot.oldAssets.replaceBy('id', event.asset)); + this.setAssets(this.snapshot.assets.replaceBy('id', event.asset)); } })); } - public setAssets(oldAssets: ImmutableArray) { - this.next(s => ({ ...s, oldAssets })); + public setAssets(assets: ImmutableArray) { + this.next(s => ({ ...s, assets })); } public pasteFiles(event: ClipboardEvent) { @@ -109,7 +109,7 @@ export class AssetsEditorComponent extends StatefulControlComponent ({ ...s, newAssets: s.newAssets.pushFront(file) })); + this.next(s => ({ ...s, assetFiles: s.assetFiles.pushFront(file) })); } } } @@ -119,13 +119,13 @@ export class AssetsEditorComponent extends StatefulControlComponent ({ ...s, newAssets: s.newAssets.pushFront(file) })); + this.next(s => ({ ...s, assetFiles: s.assetFiles.pushFront(file) })); } } } public selectAssets(assets: AssetDto[]) { - this.setAssets(this.snapshot.oldAssets.push(...assets)); + this.setAssets(this.snapshot.assets.push(...assets)); if (assets.length > 0) { this.updateValue(); @@ -138,8 +138,8 @@ export class AssetsEditorComponent extends StatefulControlComponent ({ ...s, - newAssets: s.newAssets.remove(file), - oldAssets: s.oldAssets.pushFront(asset) + assetFiles: s.assetFiles.remove(file), + assets: s.assets.pushFront(asset) })); this.updateValue(); @@ -156,14 +156,14 @@ export class AssetsEditorComponent extends StatefulControlComponent ({ ...s, newAssets: s.newAssets.remove(file) })); + this.next(s => ({ ...s, assetFiles: s.assetFiles.remove(file) })); } public changeView(isListView: boolean) { @@ -173,7 +173,7 @@ export class AssetsEditorComponent extends StatefulControlComponent x.id); + let ids: string[] | null = this.snapshot.assets.values.map(x => x.id); if (ids.length === 0) { ids = null; diff --git a/src/Squidex/app/features/content/shared/preview-button.component.html b/src/Squidex/app/features/content/shared/preview-button.component.html index 013dc9abb..1c189d2c4 100644 --- a/src/Squidex/app/features/content/shared/preview-button.component.html +++ b/src/Squidex/app/features/content/shared/preview-button.component.html @@ -1,16 +1,16 @@ - + Preview:
- - diff --git a/src/Squidex/app/features/content/shared/preview-button.component.ts b/src/Squidex/app/features/content/shared/preview-button.component.ts index e47e393fd..197fe8b65 100644 --- a/src/Squidex/app/features/content/shared/preview-button.component.ts +++ b/src/Squidex/app/features/content/shared/preview-button.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ContentDto, @@ -13,9 +13,16 @@ import { interpolate, LocalStoreService, ModalModel, - SchemaDetailsDto + SchemaDetailsDto, + StatefulComponent } from '@app/shared'; +interface State { + selectedName?: string; + + alternativeNames: string[]; +} + @Component({ selector: 'sqx-preview-button', styleUrls: ['./preview-button.component.scss'], @@ -25,7 +32,7 @@ import { ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class PreviewButtonComponent implements OnInit { +export class PreviewButtonComponent extends StatefulComponent implements OnInit { @Input() public content: ContentDto; @@ -34,13 +41,12 @@ export class PreviewButtonComponent implements OnInit { public dropdown = new ModalModel(); - public selectedName: string | undefined; - - public alternativeNames: string[]; - - constructor( + constructor(changeDetector: ChangeDetectorRef, private readonly localStore: LocalStoreService ) { + super(changeDetector, { + alternativeNames: [] + }); } public ngOnInit() { @@ -62,16 +68,23 @@ export class PreviewButtonComponent implements OnInit { } private selectUrl(selectedName: string) { - if (this.selectedName !== selectedName) { + this.next(s => { + if (selectedName === s.selectedName) { + return s; + } + const state = { ...s }; + const keys = Object.keys(this.schema.previewUrls); - this.selectedName = selectedName; + state.selectedName = selectedName; - this.alternativeNames = keys.filter(x => x !== this.selectedName); - this.alternativeNames.sort(); + state.alternativeNames = keys.filter(x => x !== s.selectedName); + state.alternativeNames.sort(); this.localStore.set(this.configKey(), selectedName); - } + + return state; + }); } private configKey() { diff --git a/src/Squidex/app/features/content/shared/references-editor.component.ts b/src/Squidex/app/features/content/shared/references-editor.component.ts index 8485eb2f9..fd5befd25 100644 --- a/src/Squidex/app/features/content/shared/references-editor.component.ts +++ b/src/Squidex/app/features/content/shared/references-editor.component.ts @@ -29,7 +29,8 @@ export const SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR: any = { }; interface State { - schema?: SchemaDetailsDto; + schema?: SchemaDetailsDto | null; + schemaInvalid: boolean; contentItems: ImmutableArray; @@ -61,6 +62,7 @@ export class ReferencesEditorComponent extends StatefulControlComponent this.usagesService.getTodayStorage(app.name))) .subscribe(dto => { @@ -123,7 +114,7 @@ export class DashboardPageComponent implements OnDestroy, OnInit { this.assetsMax = dto.maxAllowed; })); - this.subscriptions.push( + this.takeOver( this.app.pipe( switchMap(app => this.usagesService.getMonthCalls(app.name))) .subscribe(dto => { @@ -131,14 +122,14 @@ export class DashboardPageComponent implements OnDestroy, OnInit { this.callsMax = dto.maxAllowed; })); - this.subscriptions.push( + this.takeOver( this.app.pipe( switchMap(app => this.historyService.getHistory(app.name, ''))) .subscribe(dto => { this.history = dto; })); - this.subscriptions.push( + this.takeOver( this.app.pipe( switchMap(app => this.usagesService.getStorageUsages(app.name, DateTime.today().addDays(-20), DateTime.today()))) .subscribe(dtos => { @@ -175,7 +166,7 @@ export class DashboardPageComponent implements OnDestroy, OnInit { }; })); - this.subscriptions.push( + this.takeOver( this.app.pipe( switchMap(app => this.usagesService.getCallsUsages(app.name, DateTime.today().addDays(-20), DateTime.today()))) .subscribe(dtos => { diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts index 4c5978824..ba66a2d37 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts @@ -9,8 +9,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; -import { filter, onErrorResumeNext } from 'rxjs/operators'; +import { onErrorResumeNext } from 'rxjs/operators'; import { AppsState, @@ -21,6 +20,7 @@ import { MessageBus, ModalModel, PatternsState, + ResourceOwner, SchemaDetailsDto, SchemasState, Types @@ -38,9 +38,7 @@ import { fadeAnimation ] }) -export class SchemaPageComponent implements OnDestroy, OnInit { - private selectedSchemaSubscription: Subscription; - +export class SchemaPageComponent extends ResourceOwner implements OnDestroy, OnInit { public fieldTypes = fieldTypes; public schemaExport: any; @@ -64,22 +62,21 @@ export class SchemaPageComponent implements OnDestroy, OnInit { private readonly router: Router, private readonly messageBus: MessageBus ) { - } - - public ngOnDestroy() { - this.selectedSchemaSubscription.unsubscribe(); + super(); } public ngOnInit() { this.patternsState.load().pipe(onErrorResumeNext()).subscribe(); - this.selectedSchemaSubscription = - this.schemasState.selectedSchema.pipe(filter(s => !!s)) + this.takeOver( + this.schemasState.selectedSchema .subscribe(schema => { - this.schema = schema!; + if (schema) { + this.schema = schema; - this.export(); - }); + this.export(); + } + })); } public publish() { diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts index 5a1394aaf..306edee1c 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts @@ -5,22 +5,24 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; -import { Observable, Subscription } from 'rxjs'; +import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; -import { FieldDto, FloatConverter, NumberFieldPropertiesDto } from '@app/shared'; +import { + FieldDto, + FloatConverter, + NumberFieldPropertiesDto, + ResourceOwner +} from '@app/shared'; @Component({ selector: 'sqx-number-ui', styleUrls: ['number-ui.component.scss'], templateUrl: 'number-ui.component.html' }) -export class NumberUIComponent implements OnDestroy, OnInit { - private hideAllowedValuesSubscription: Subscription; - private hideInlineEditableSubscription: Subscription; - +export class NumberUIComponent extends ResourceOwner implements OnInit { @Input() public editForm: FormGroup; @@ -35,11 +37,6 @@ export class NumberUIComponent implements OnDestroy, OnInit { public hideAllowedValues: Observable; public hideInlineEditable: Observable; - public ngOnDestroy() { - this.hideAllowedValuesSubscription.unsubscribe(); - this.hideInlineEditableSubscription.unsubscribe(); - } - public ngOnInit() { this.editForm.setControl('editor', new FormControl(this.properties.editor, [ @@ -60,18 +57,18 @@ export class NumberUIComponent implements OnDestroy, OnInit { this.editForm.controls['editor'].valueChanges.pipe( startWith(this.properties.editor), map(x => !(x && (x === 'Input' || x === 'Dropdown')))); - this.hideAllowedValuesSubscription = + this.takeOver( this.hideAllowedValues.subscribe(isSelection => { if (isSelection) { this.editForm.controls['allowedValues'].setValue(undefined); } - }); + })); - this.hideInlineEditableSubscription = + this.takeOver( this.hideInlineEditable.subscribe(isSelection => { if (isSelection) { this.editForm.controls['inlineEditable'].setValue(false); } - }); + })); } } \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts index cee172769..8e1ad06dd 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts @@ -5,22 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; -import { Observable, Subscription } from 'rxjs'; +import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; -import { FieldDto, StringFieldPropertiesDto } from '@app/shared'; +import { + FieldDto, + ResourceOwner, + StringFieldPropertiesDto +} from '@app/shared'; @Component({ selector: 'sqx-string-ui', styleUrls: ['string-ui.component.scss'], templateUrl: 'string-ui.component.html' }) -export class StringUIComponent implements OnDestroy, OnInit { - private hideAllowedValuesSubscription: Subscription; - private hideInlineEditableSubscription: Subscription; - +export class StringUIComponent extends ResourceOwner implements OnInit { @Input() public editForm: FormGroup; @@ -33,11 +34,6 @@ export class StringUIComponent implements OnDestroy, OnInit { public hideAllowedValues: Observable; public hideInlineEditable: Observable; - public ngOnDestroy() { - this.hideAllowedValuesSubscription.unsubscribe(); - this.hideInlineEditableSubscription.unsubscribe(); - } - public ngOnInit() { this.editForm.setControl('editor', new FormControl(this.properties.editor, [ @@ -58,18 +54,18 @@ export class StringUIComponent implements OnDestroy, OnInit { this.editForm.controls['editor'].valueChanges.pipe( startWith(this.properties.editor), map(x => !(x && (x === 'Input' || x === 'Dropdown' || x === 'Slug')))); - this.hideAllowedValuesSubscription = + this.takeOver( this.hideAllowedValues.subscribe(isSelection => { if (isSelection) { this.editForm.controls['allowedValues'].setValue(undefined); } - }); + })); - this.hideInlineEditableSubscription = + this.takeOver( this.hideInlineEditable.subscribe(isSelection => { if (isSelection) { this.editForm.controls['inlineEditable'].setValue(false); } - }); + })); } } \ No newline at end of file diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts index 3f1c137a0..ed7cd48e4 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts @@ -7,7 +7,7 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; -import { Observable, Subscription } from 'rxjs'; +import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { @@ -15,6 +15,7 @@ import { FieldDto, ImmutableArray, ModalModel, + ResourceOwner, RootFieldDto, StringFieldPropertiesDto, Types @@ -25,9 +26,7 @@ import { styleUrls: ['string-validation.component.scss'], templateUrl: 'string-validation.component.html' }) -export class StringValidationComponent implements OnDestroy, OnInit { - private patternSubscription: Subscription; - +export class StringValidationComponent extends ResourceOwner implements OnDestroy, OnInit { @Input() public editForm: FormGroup; @@ -49,10 +48,6 @@ export class StringValidationComponent implements OnDestroy, OnInit { public showUnique: boolean; - public ngOnDestroy() { - this.patternSubscription.unsubscribe(); - } - public ngOnInit() { this.showUnique = Types.is(this.field, RootFieldDto) && !this.field.isLocalizable; @@ -87,14 +82,15 @@ export class StringValidationComponent implements OnDestroy, OnInit { this.showPatternMessage = this.editForm.controls['pattern'].value && this.editForm.controls['pattern'].value.trim().length > 0; - this.patternSubscription = + this.takeOver( this.editForm.controls['pattern'].valueChanges .subscribe((value: string) => { if (!value || value.length === 0) { this.editForm.controls['patternMessage'].setValue(undefined); } + this.setPatternName(); - }); + })); this.setPatternName(); } diff --git a/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts b/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts index 6b212f8b5..62bc3ec20 100644 --- a/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts +++ b/src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts @@ -5,10 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; import { map, onErrorResumeNext } from 'rxjs/operators'; import { @@ -16,6 +15,7 @@ import { CreateCategoryForm, DialogModel, MessageBus, + ResourceOwner, SchemaDto, SchemasState } from '@app/shared'; @@ -27,9 +27,7 @@ import { SchemaCloning } from './../messages'; styleUrls: ['./schemas-page.component.scss'], templateUrl: './schemas-page.component.html' }) -export class SchemasPageComponent implements OnDestroy, OnInit { - private schemaCloningSubscription: Subscription; - +export class SchemasPageComponent extends ResourceOwner implements OnInit { public addSchemaDialog = new DialogModel(); public addCategoryForm = new CreateCategoryForm(this.formBuilder); @@ -45,27 +43,25 @@ export class SchemasPageComponent implements OnDestroy, OnInit { private readonly route: ActivatedRoute, private readonly router: Router ) { - } - - public ngOnDestroy() { - this.schemaCloningSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.schemaCloningSubscription = + this.takeOver( this.messageBus.of(SchemaCloning) .subscribe(m => { this.import = m.schema; this.addSchemaDialog.show(); - }); - - this.route.params.pipe(map(q => q['showDialog'])) - .subscribe(showDialog => { - if (showDialog) { - this.addSchemaDialog.show(); - } - }); + })); + + this.takeOver( + this.route.params.pipe(map(q => q['showDialog'])) + .subscribe(showDialog => { + if (showDialog) { + this.addSchemaDialog.show(); + } + })); this.schemasState.load().pipe(onErrorResumeNext()).subscribe(); } diff --git a/src/Squidex/app/features/settings/pages/backups/backups-page.component.ts b/src/Squidex/app/features/settings/pages/backups/backups-page.component.ts index b2ef916a0..3ae10a231 100644 --- a/src/Squidex/app/features/settings/pages/backups/backups-page.component.ts +++ b/src/Squidex/app/features/settings/pages/backups/backups-page.component.ts @@ -5,14 +5,15 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Subscription, timer } from 'rxjs'; +import { Component, OnInit } from '@angular/core'; +import { timer } from 'rxjs'; import { onErrorResumeNext, switchMap } from 'rxjs/operators'; import { AppsState, BackupDto, - BackupsState + BackupsState, + ResourceOwner } from '@app/shared'; @Component({ @@ -20,25 +21,20 @@ import { styleUrls: ['./backups-page.component.scss'], templateUrl: './backups-page.component.html' }) -export class BackupsPageComponent implements OnInit, OnDestroy { - private timerSubscription: Subscription; - +export class BackupsPageComponent extends ResourceOwner implements OnInit { constructor( public readonly appsState: AppsState, public readonly backupsState: BackupsState ) { - } - - public ngOnDestroy() { - this.timerSubscription.unsubscribe(); + super(); } public ngOnInit() { this.backupsState.load().pipe(onErrorResumeNext()).subscribe(); - this.timerSubscription = - timer(3000, 3000).pipe(switchMap(t => this.backupsState.load(true, true).pipe(onErrorResumeNext()))) - .subscribe(); + this.takeOver( + timer(3000, 3000).pipe(switchMap(() => this.backupsState.load(true, true).pipe(onErrorResumeNext()))) + .subscribe()); } public reload() { @@ -53,7 +49,7 @@ export class BackupsPageComponent implements OnInit, OnDestroy { this.backupsState.delete(backup).pipe(onErrorResumeNext()).subscribe(); } - public trackByBackup(index: number, item: BackupDto) { + public trackByBackup(item: BackupDto) { return item.id; } } diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts index fddd713e5..b540f1b75 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts @@ -8,7 +8,7 @@ import { Component, Injectable, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { Observable } from 'rxjs'; -import { filter, onErrorResumeNext, withLatestFrom } from 'rxjs/operators'; +import { onErrorResumeNext, withLatestFrom } from 'rxjs/operators'; import { AppContributorDto, @@ -34,7 +34,7 @@ export class UsersDataSource implements AutocompleteSource { public find(query: string): Observable { return this.usersService.getUsers(query).pipe( - withLatestFrom(this.contributorsState.contributors.pipe(filter(x => !!x)), (users, contributors) => { + withLatestFrom(this.contributorsState.contributors, (users, contributors) => { const results: any[] = []; for (let user of users) { diff --git a/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts b/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts index 179323835..a56c5b648 100644 --- a/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts +++ b/src/Squidex/app/features/settings/pages/languages/languages-page.component.ts @@ -5,16 +5,16 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; -import { Subscription } from 'rxjs'; import { onErrorResumeNext } from 'rxjs/operators'; import { AddLanguageForm, AppLanguageDto, AppsState, - LanguagesState + LanguagesState, + ResourceOwner } from '@app/shared'; @Component({ @@ -22,9 +22,7 @@ import { styleUrls: ['./languages-page.component.scss'], templateUrl: './languages-page.component.html' }) -export class LanguagesPageComponent implements OnDestroy, OnInit { - private newLanguagesSubscription: Subscription; - +export class LanguagesPageComponent extends ResourceOwner implements OnInit { public addLanguageForm = new AddLanguageForm(this.formBuilder); constructor( @@ -32,20 +30,17 @@ export class LanguagesPageComponent implements OnDestroy, OnInit { public readonly languagesState: LanguagesState, private readonly formBuilder: FormBuilder ) { - } - - public ngOnDestroy() { - this.newLanguagesSubscription.unsubscribe(); + super(); } public ngOnInit() { - this.newLanguagesSubscription = + this.takeOver( this.languagesState.newLanguages .subscribe(languages => { if (languages.length > 0) { this.addLanguageForm.load({ language: languages.at(0) }); } - }); + })); this.languagesState.load().pipe(onErrorResumeNext()).subscribe(); } @@ -67,7 +62,7 @@ export class LanguagesPageComponent implements OnDestroy, OnInit { } } - public trackByLanguage(index: number, language: { language: AppLanguageDto }) { + public trackByLanguage(language: { language: AppLanguageDto }) { return language.language; } } diff --git a/src/Squidex/app/framework/angular/forms/autocomplete.component.ts b/src/Squidex/app/framework/angular/forms/autocomplete.component.ts index 580593e34..5586ef6d4 100644 --- a/src/Squidex/app/framework/angular/forms/autocomplete.component.ts +++ b/src/Squidex/app/framework/angular/forms/autocomplete.component.ts @@ -10,7 +10,7 @@ import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { catchError, debounceTime, distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators'; -import { StatefulControlComponent } from '@app/shared'; +import { StatefulControlComponent } from '@app/framework/internal'; export interface AutocompleteSource { find(query: string): Observable; @@ -27,6 +27,7 @@ export const SQX_AUTOCOMPLETE_CONTROL_VALUE_ACCESSOR: any = { interface State { suggestedItems: any[]; + suggestedIndex: number; } @@ -66,7 +67,7 @@ export class AutocompleteComponent extends StatefulControlComponent { this.callChange(query); diff --git a/src/Squidex/app/framework/angular/forms/code-editor.component.ts b/src/Squidex/app/framework/angular/forms/code-editor.component.ts index 056ebfe55..6d99cdef0 100644 --- a/src/Squidex/app/framework/angular/forms/code-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/code-editor.component.ts @@ -41,8 +41,7 @@ export class CodeEditorComponent extends ExternalControlComponent implem @Input() public mode = 'ace/mode/javascript'; - constructor( - changeDetector: ChangeDetectorRef, + constructor(changeDetector: ChangeDetectorRef, private readonly resourceLoader: ResourceLoaderService ) { super(changeDetector); diff --git a/src/Squidex/app/framework/angular/forms/control-errors.component.ts b/src/Squidex/app/framework/angular/forms/control-errors.component.ts index 26f8a0ce7..cdafba5b4 100644 --- a/src/Squidex/app/framework/angular/forms/control-errors.component.ts +++ b/src/Squidex/app/framework/angular/forms/control-errors.component.ts @@ -91,7 +91,7 @@ export class ControlErrorsComponent extends StatefulComponent implements this.control = control; if (control) { - this.observe( + this.takeOver( merge(control.valueChanges, control.statusChanges) .subscribe(() => { this.createMessages(); @@ -127,6 +127,6 @@ export class ControlErrorsComponent extends StatefulComponent implements } } - this.next(() => ({ errorMessages })); + this.next(s => ({ ...s, errorMessages })); } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts b/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts index cca58a42b..cb7c39b86 100644 --- a/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/date-time-editor.component.ts @@ -60,7 +60,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string } public ngOnInit() { - this.observe( + this.takeOver( this.timeControl.valueChanges.subscribe(value => { if (!value || value.length === 0) { this.timeValue = null; @@ -71,7 +71,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string this.updateValue(); })); - this.observe( + this.takeOver( this.dateControl.valueChanges.subscribe(value => { if (!value || value.length === 0) { this.dateValue = null; diff --git a/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts b/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts index 11146a73c..7573ea6a7 100644 --- a/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/iframe-editor.component.ts @@ -50,7 +50,7 @@ export class IFrameEditorComponent extends ExternalControlComponent impleme } public ngOnInit(): void { - this.observe( + this.takeOver( this.renderer.listen('window', 'message', (event: MessageEvent) => { if (event.source === this.plugin.contentWindow) { const { type } = event.data; diff --git a/src/Squidex/app/framework/angular/forms/json-editor.component.ts b/src/Squidex/app/framework/angular/forms/json-editor.component.ts index 143fd095a..e2baa6854 100644 --- a/src/Squidex/app/framework/angular/forms/json-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/json-editor.component.ts @@ -39,6 +39,8 @@ export class JsonEditorComponent extends ExternalControlComponent implem private readonly resourceLoader: ResourceLoaderService ) { super(changeDetector); + + changeDetector.detach(); } public writeValue(obj: any) { diff --git a/src/Squidex/app/framework/angular/forms/stars.component.ts b/src/Squidex/app/framework/angular/forms/stars.component.ts index 69a0e24ca..59be6fac2 100644 --- a/src/Squidex/app/framework/angular/forms/stars.component.ts +++ b/src/Squidex/app/framework/angular/forms/stars.component.ts @@ -16,6 +16,7 @@ export const SQX_STARS_CONTROL_VALUE_ACCESSOR: any = { interface State { stars: number; + starsArray: number[]; value: number | null; diff --git a/src/Squidex/app/framework/angular/forms/tag-editor.component.html b/src/Squidex/app/framework/angular/forms/tag-editor.component.html index 36d93bf22..a49056221 100644 --- a/src/Squidex/app/framework/angular/forms/tag-editor.component.html +++ b/src/Squidex/app/framework/angular/forms/tag-editor.component.html @@ -2,7 +2,7 @@
+ [class.disabled]="addInput.disabled"> {{item}} diff --git a/src/Squidex/app/framework/angular/forms/tag-editor.component.ts b/src/Squidex/app/framework/angular/forms/tag-editor.component.ts index 8927195c7..ee11740f8 100644 --- a/src/Squidex/app/framework/angular/forms/tag-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/tag-editor.component.ts @@ -78,6 +78,7 @@ interface State { hasFocus: boolean; suggestedItems: string[]; + suggestedIndex: number; items: any[]; @@ -146,7 +147,7 @@ export class TagEditorComponent extends StatefulControlComponent i } public ngOnInit() { - this.observe( + this.takeOver( this.addInput.valueChanges.pipe( tap(() => { this.resetSize(); diff --git a/src/Squidex/app/framework/angular/forms/toggle.component.ts b/src/Squidex/app/framework/angular/forms/toggle.component.ts index 5eb23cb82..7105ada32 100644 --- a/src/Squidex/app/framework/angular/forms/toggle.component.ts +++ b/src/Squidex/app/framework/angular/forms/toggle.component.ts @@ -8,9 +8,7 @@ import { ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Types } from '@app/framework/internal'; - -import { StatefulControlComponent } from '../stateful.component'; +import { StatefulControlComponent, Types } from '@app/framework/internal'; export const SQX_TOGGLE_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ToggleComponent), multi: true diff --git a/src/Squidex/app/framework/angular/ignore-scrollbar.directive.ts b/src/Squidex/app/framework/angular/ignore-scrollbar.directive.ts index 384bcb25c..8de848e47 100644 --- a/src/Squidex/app/framework/angular/ignore-scrollbar.directive.ts +++ b/src/Squidex/app/framework/angular/ignore-scrollbar.directive.ts @@ -5,27 +5,23 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { AfterViewInit, Directive, ElementRef, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, OnInit, Renderer2 } from '@angular/core'; +import { timer } from 'rxjs'; + +import { ResourceOwner } from '@app/framework/internal'; @Directive({ selector: '[sqxIgnoreScrollbar]' }) -export class IgnoreScrollbarDirective implements OnDestroy, OnInit, AfterViewInit { - private resizeListener: Function; +export class IgnoreScrollbarDirective extends ResourceOwner implements OnInit, AfterViewInit { private parent: any; - private checkTimer: any; private scollbarWidth = 0; constructor( private readonly element: ElementRef, private readonly renderer: Renderer2 ) { - } - - public ngOnDestroy() { - clearTimeout(this.checkTimer); - - this.resizeListener(); + super(); } public ngOnInit() { @@ -33,15 +29,12 @@ export class IgnoreScrollbarDirective implements OnDestroy, OnInit, AfterViewIni this.parent = this.renderer.parentNode(this.element.nativeElement); } - this.resizeListener = + this.takeOver( this.renderer.listen(this.element.nativeElement, 'resize', () => { this.reposition(); - }); + })); - this.checkTimer = - setTimeout(() => { - this.reposition(); - }, 100); + this.takeOver(timer(100, 100).subscribe(() => this.reposition)); } public ngAfterViewInit() { diff --git a/src/Squidex/app/framework/angular/image-source.directive.ts b/src/Squidex/app/framework/angular/image-source.directive.ts index cdfb16fd7..7b45bfd4f 100644 --- a/src/Squidex/app/framework/angular/image-source.directive.ts +++ b/src/Squidex/app/framework/angular/image-source.directive.ts @@ -7,18 +7,16 @@ import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, Renderer2 } from '@angular/core'; -import { MathHelper } from '@app/framework/internal'; +import { MathHelper, ResourceOwner } from '@app/framework/internal'; const LAYOUT_CACHE: { [key: string]: { width: number, height: number } } = {}; @Directive({ selector: '[sqxImageSource]' }) -export class ImageSourceDirective implements OnChanges, OnDestroy, OnInit, AfterViewInit { - private parentResizeListener: Function; - - private loadingTimer: any; +export class ImageSourceDirective extends ResourceOwner implements OnChanges, OnDestroy, OnInit, AfterViewInit { private size: any; + private loadTimer: any; private loadRetries = 0; private loadQuery: string | null = null; @@ -38,12 +36,13 @@ export class ImageSourceDirective implements OnChanges, OnDestroy, OnInit, After private readonly element: ElementRef, private readonly renderer: Renderer2 ) { + super(); } public ngOnDestroy() { - clearTimeout(this.loadingTimer); + super.ngOnDestroy(); - this.parentResizeListener(); + clearTimeout(this.loadTimer); } public ngOnInit() { @@ -51,10 +50,10 @@ export class ImageSourceDirective implements OnChanges, OnDestroy, OnInit, After this.parent = this.renderer.parentNode(this.element.nativeElement); } - this.parentResizeListener = + this.takeOver( this.renderer.listen(this.parent, 'resize', () => { this.resize(); - }); + })); } public ngAfterViewInit() { @@ -127,7 +126,7 @@ export class ImageSourceDirective implements OnChanges, OnDestroy, OnInit, After this.loadRetries++; if (this.loadRetries <= 10) { - this.loadingTimer = + this.loadTimer = setTimeout(() => { this.loadQuery = MathHelper.guid(); diff --git a/src/Squidex/app/framework/angular/modals/dialog-renderer.component.ts b/src/Squidex/app/framework/angular/modals/dialog-renderer.component.ts index 666da8e15..42dff0f73 100644 --- a/src/Squidex/app/framework/angular/modals/dialog-renderer.component.ts +++ b/src/Squidex/app/framework/angular/modals/dialog-renderer.component.ts @@ -13,11 +13,10 @@ import { DialogRequest, DialogService, fadeAnimation, - Notification + Notification, + StatefulComponent } from '@app/framework/internal'; -import { StatefulComponent } from '../stateful.component'; - interface State { dialogRequest?: DialogRequest | null; @@ -46,14 +45,14 @@ export class DialogRendererComponent extends StatefulComponent implements } public ngOnInit() { - this.observe( + this.takeOver( this.dialogView.isOpen.subscribe(isOpen => { if (!isOpen) { this.finishRequest(false); } })); - this.observe( + this.takeOver( this.dialogs.notifications.subscribe(notification => { this.next(s => ({ ...s, @@ -61,13 +60,13 @@ export class DialogRendererComponent extends StatefulComponent implements })); if (notification.displayTime > 0) { - this.observe(timer(notification.displayTime).subscribe(() => { + this.takeOver(timer(notification.displayTime).subscribe(() => { this.close(notification); })); } })); - this.observe( + this.takeOver( this.dialogs.dialogs .subscribe(dialogRequest => { this.cancel(); diff --git a/src/Squidex/app/framework/angular/modals/modal-dialog.component.html b/src/Squidex/app/framework/angular/modals/modal-dialog.component.html index d7f3e5955..bf7383621 100644 --- a/src/Squidex/app/framework/angular/modals/modal-dialog.component.html +++ b/src/Squidex/app/framework/angular/modals/modal-dialog.component.html @@ -13,7 +13,7 @@
- -