diff --git a/src/Squidex/app/features/administration/guards/user-must-exist.guard.ts b/src/Squidex/app/features/administration/guards/user-must-exist.guard.ts index db67514f9..63fc2bba1 100644 --- a/src/Squidex/app/features/administration/guards/user-must-exist.guard.ts +++ b/src/Squidex/app/features/administration/guards/user-must-exist.guard.ts @@ -32,7 +32,7 @@ export class UserMustExistGuard implements CanActivate { this.router.navigate(['/404']); } }), - map(u => u !== null)); + map(u => !!u)); return result; } 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 30161a18b..a8df38179 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 @@ -38,8 +38,7 @@ export class EventConsumersPageComponent implements OnDestroy, OnInit { this.eventConsumersState.load(false, true).pipe(onErrorResumeNext()).subscribe(); this.timerSubscription = - timer(2000, 2000).pipe( - switchMap(x => this.eventConsumersState.load(true, true)), onErrorResumeNext()) + timer(2000, 2000).pipe(switchMap(x => this.eventConsumersState.load(true, true)), onErrorResumeNext()) .subscribe(); } 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 fa8977ccf..32884a7d2 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 @@ -8,7 +8,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { Subscription, timer } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { filter, switchMap } from 'rxjs/operators'; import { AuthService, @@ -43,11 +43,9 @@ export class RestorePageComponent implements OnDestroy, OnInit { public ngOnInit() { this.timerSubscription = - timer(0, 2000).pipe(switchMap(() => this.backupsService.getRestore())) + timer(0, 2000).pipe(switchMap(() => this.backupsService.getRestore()), filter(x => !!x)) .subscribe(dto => { - if (dto !== null) { - this.restoreJob = dto; - } + this.restoreJob = dto!; }); } diff --git a/src/Squidex/app/features/content/module.ts b/src/Squidex/app/features/content/module.ts index 832b7c161..f08988fc9 100644 --- a/src/Squidex/app/features/content/module.ts +++ b/src/Squidex/app/features/content/module.ts @@ -15,6 +15,7 @@ import { ContentMustExistGuard, LoadLanguagesGuard, SchemaMustExistPublishedGuard, + SchemaMustNotBeSingletonGuard, SqxFrameworkModule, SqxSharedModule, UnsetContentGuard @@ -52,12 +53,13 @@ const routes: Routes = [ { path: '', component: ContentsPageComponent, + canActivate: [SchemaMustNotBeSingletonGuard], canDeactivate: [CanDeactivateGuard] }, { path: 'new', component: ContentPageComponent, - canActivate: [UnsetContentGuard], + canActivate: [SchemaMustNotBeSingletonGuard, UnsetContentGuard], canDeactivate: [CanDeactivateGuard] }, { 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 d8466fdcd..53b9f47b9 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 @@ -8,7 +8,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, of, Subscription } from 'rxjs'; -import { filter, map, onErrorResumeNext, switchMap } from 'rxjs/operators'; +import { filter, onErrorResumeNext, switchMap } from 'rxjs/operators'; import { ContentVersionSelected } from './../messages'; @@ -88,19 +88,19 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, }); this.selectedSchemaSubscription = - this.schemasState.selectedSchema.pipe(filter(s => !!s), map(s => s!)) + this.schemasState.selectedSchema.pipe(filter(s => !!s)) .subscribe(schema => { - this.schema = schema; + this.schema = schema!; this.contentForm = new EditContentForm(this.schema, this.languages); }); this.contentSubscription = - this.contentsState.selectedContent.pipe(filter(c => !!c), map(c => c!)) + this.contentsState.selectedContent.pipe(filter(c => !!c)) .subscribe(content => { - this.content = content; + this.content = content!; - this.loadContent(content.dataDraft); + this.loadContent(this.content.dataDraft); }); this.contentVersionSelectedSubscription = 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 5f68b0ab2..b3a3acbd4 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 @@ -6,8 +6,9 @@ */ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; -import { onErrorResumeNext, switchMap, tap } from 'rxjs/operators'; +import { filter, onErrorResumeNext, switchMap, takeUntil, tap } from 'rxjs/operators'; import { AppLanguageDto, @@ -17,6 +18,7 @@ import { ImmutableArray, LanguagesState, ModalModel, + navigatedToOtherComponent, Queries, SchemaDetailsDto, SchemasState, @@ -59,6 +61,7 @@ export class ContentsPageComponent implements OnDestroy, OnInit { public readonly contentsState: ContentsState, private readonly languagesState: LanguagesState, private readonly schemasState: SchemasState, + private readonly router: Router, private readonly uiState: UIState ) { } @@ -70,8 +73,10 @@ export class ContentsPageComponent implements OnDestroy, OnInit { } public ngOnInit() { + const routeChanged = this.router.events.pipe(filter(navigatedToOtherComponent(this.router))); + this.selectedSchemaSubscription = - this.schemasState.selectedSchema + this.schemasState.selectedSchema.pipe(takeUntil(routeChanged)) .subscribe(schema => { this.resetSelection(); @@ -82,7 +87,7 @@ export class ContentsPageComponent implements OnDestroy, OnInit { }); this.contentsSubscription = - this.contentsState.contents + this.contentsState.contents.pipe(takeUntil(routeChanged)) .subscribe(() => { this.updateSelectionSummary(); }); @@ -100,7 +105,7 @@ export class ContentsPageComponent implements OnDestroy, OnInit { } public deleteSelected() { - this.contentsState.deleteMany(this.select()).pipe(onErrorResumeNext()).subscribe(); + this.contentsState.deleteMany(this.selectItems()).pipe(onErrorResumeNext()).subscribe(); } public delete(content: ContentDto) { @@ -112,7 +117,7 @@ export class ContentsPageComponent implements OnDestroy, OnInit { } public publishSelected() { - this.changeContentItems(this.select(c => c.status !== 'Published'), 'Publish'); + this.changeContentItems(this.selectItems(c => c.status !== 'Published'), 'Publish'); } public unpublish(content: ContentDto) { @@ -120,7 +125,7 @@ export class ContentsPageComponent implements OnDestroy, OnInit { } public unpublishSelected() { - this.changeContentItems(this.select(c => c.status === 'Published'), 'Unpublish'); + this.changeContentItems(this.selectItems(c => c.status === 'Published'), 'Unpublish'); } public archive(content: ContentDto) { @@ -128,15 +133,15 @@ export class ContentsPageComponent implements OnDestroy, OnInit { } public archiveSelected() { - this.changeContentItems(this.select(), 'Archive'); + this.changeContentItems(this.selectItems(), 'Archive'); } public restore(content: ContentDto) { this.changeContentItems([content], 'Restore'); } - public restoreSelected(scheduled: boolean) { - this.changeContentItems(this.select(), 'Restore'); + public restoreSelected() { + this.changeContentItems(this.selectItems(), 'Restore'); } public clone(content: ContentDto) { @@ -185,12 +190,16 @@ export class ContentsPageComponent implements OnDestroy, OnInit { this.contentsState.search(query).pipe(onErrorResumeNext()).subscribe(); } + public selectLanguage(language: AppLanguageDto) { + this.language = language; + } + public isItemSelected(content: ContentDto): boolean { return !!this.selectedItems[content.id]; } - public selectLanguage(language: AppLanguageDto) { - this.language = language; + private selectItems(predicate?: (content: ContentDto) => boolean) { + return this.contentsState.snapshot.contents.values.filter(c => this.selectedItems[c.id] && (!predicate || predicate(c))); } public selectItem(content: ContentDto, isSelected: boolean) { @@ -215,10 +224,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit { return content.id; } - private select(predicate?: (content: ContentDto) => boolean) { - return this.contentsState.snapshot.contents.values.filter(c => this.selectedItems[c.id] && (!predicate || predicate(c))); - } - private resetSelection() { this.selectedItems = {}; diff --git a/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html b/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html index 1903873f2..a270463cd 100644 --- a/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html +++ b/src/Squidex/app/features/content/pages/schemas/schemas-page.component.html @@ -21,6 +21,7 @@ [name]="category" [schemas]="schemas" [schemasFilter]="schemasFilter.valueChanges | async" + [routeSingletonToContent]="true" [isReadonly]="true"> diff --git a/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.ts b/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.ts index 0405d9426..ba2eaad1d 100644 --- a/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.ts +++ b/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.ts @@ -82,7 +82,7 @@ export class ContentChangedTriggerComponent implements OnInit { } else { return null; } - }).filter(s => s !== null).map(s => s!)).sortByStringAsc(s => s.schema.name); + }).filter(s => !!s).map(s => s!)).sortByStringAsc(s => s.schema.name); this.schemasToAdd = this.schemas.filter(schema => 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 92ab4699e..81c86168f 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 @@ -10,7 +10,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Subscription } from 'rxjs'; -import { filter, map, onErrorResumeNext } from 'rxjs/operators'; +import { filter, onErrorResumeNext } from 'rxjs/operators'; import { AppsState, @@ -73,9 +73,9 @@ export class SchemaPageComponent implements OnDestroy, OnInit { this.patternsState.load().pipe(onErrorResumeNext()).subscribe(); this.selectedSchemaSubscription = - this.schemasState.selectedSchema.pipe(filter(s => !!s), map(s => s!)) + this.schemasState.selectedSchema.pipe(filter(s => !!s)) .subscribe(schema => { - this.schema = schema; + this.schema = schema!; this.export(); }); 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 6dbc72276..85f2d2b18 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 @@ -52,7 +52,7 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } public get hasValue() { - return this.dateValue !== null; + return !!this.dateValue; } @ViewChild('dateInput') diff --git a/src/Squidex/app/framework/angular/forms/stars.component.ts b/src/Squidex/app/framework/angular/forms/stars.component.ts index 0b3b0c728..b3b17eb9c 100644 --- a/src/Squidex/app/framework/angular/forms/stars.component.ts +++ b/src/Squidex/app/framework/angular/forms/stars.component.ts @@ -88,7 +88,7 @@ export class StarsComponent implements ControlValueAccessor { return false; } - if (this.value !== null) { + if (this.value) { this.value = null; this.stars = 0; diff --git a/src/Squidex/app/framework/angular/image-source.directive.ts b/src/Squidex/app/framework/angular/image-source.directive.ts index 2bc972977..ecb517ccc 100644 --- a/src/Squidex/app/framework/angular/image-source.directive.ts +++ b/src/Squidex/app/framework/angular/image-source.directive.ts @@ -96,7 +96,7 @@ export class ImageSourceDirective implements OnChanges, OnDestroy, OnInit, After if (w > 0 && h > 0) { let source = `${this.imageSource}&width=${w}&height=${h}&mode=Crop`; - if (this.loadQuery !== null) { + if (this.loadQuery) { source += `&q=${this.loadQuery}`; } diff --git a/src/Squidex/app/framework/angular/modals/modal-view.directive.ts b/src/Squidex/app/framework/angular/modals/modal-view.directive.ts index 160488675..16a4834d3 100644 --- a/src/Squidex/app/framework/angular/modals/modal-view.directive.ts +++ b/src/Squidex/app/framework/angular/modals/modal-view.directive.ts @@ -71,7 +71,7 @@ export class ModalViewDirective implements OnChanges, OnDestroy { } private update(isOpen: boolean) { - if (isOpen === (this.renderedView !== null)) { + if (isOpen === (!!this.renderedView)) { return; } diff --git a/src/Squidex/app/framework/angular/routers/router-utils.ts b/src/Squidex/app/framework/angular/routers/router-utils.ts index 6dbfe0831..4dd16a7a8 100644 --- a/src/Squidex/app/framework/angular/routers/router-utils.ts +++ b/src/Squidex/app/framework/angular/routers/router-utils.ts @@ -5,7 +5,9 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ActivatedRoute, ActivatedRouteSnapshot, Data, Params } from '@angular/router'; +import { ActivatedRoute, ActivatedRouteSnapshot, Data, Params, Router, RouterEvent, RouterStateSnapshot, RoutesRecognized } from '@angular/router'; + +import { Types } from './../../utils/types'; export function allData(value: ActivatedRouteSnapshot | ActivatedRoute): Data { let snapshot: ActivatedRouteSnapshot | null = value['snapshot'] || value; @@ -40,4 +42,22 @@ export function allParams(value: ActivatedRouteSnapshot | ActivatedRoute): Param } return result; +} + +export function childComponent(value: RouterStateSnapshot) { + let current = value.root; + + while (true) { + if (current.firstChild) { + current = current.firstChild; + } else { + break; + } + } + + return current.component; +} + +export function navigatedToOtherComponent(router: Router) { + return (e: RouterEvent) => Types.is(e, RoutesRecognized) && childComponent(e.state) !== childComponent(router.routerState.snapshot); } \ No newline at end of file diff --git a/src/Squidex/app/shared/components/geolocation-editor.component.ts b/src/Squidex/app/shared/components/geolocation-editor.component.ts index 7239840db..2b4709ce3 100644 --- a/src/Squidex/app/shared/components/geolocation-editor.component.ts +++ b/src/Squidex/app/shared/components/geolocation-editor.component.ts @@ -144,8 +144,9 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi } public updateValueByInput() { - let updateMap = this.geolocationForm.controls['latitude'].value !== null && - this.geolocationForm.controls['longitude'].value !== null; + let updateMap = + !!this.geolocationForm.controls['latitude'].value && + !!this.geolocationForm.controls['longitude'].value; this.value = this.geolocationForm.value; diff --git a/src/Squidex/app/shared/components/schema-category.component.html b/src/Squidex/app/shared/components/schema-category.component.html index 570ae4798..f86bc7c42 100644 --- a/src/Squidex/app/shared/components/schema-category.component.html +++ b/src/Squidex/app/shared/components/schema-category.component.html @@ -16,7 +16,7 @@