From 2df3ce73af63ba36d8960f838adceee3b78d8929 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 9 Sep 2017 22:33:32 +0200 Subject: [PATCH] TS improvements --- .../pages/content/content-page.component.ts | 2 +- .../pages/contents/contents-page.component.ts | 4 +- .../content/shared/assets-editor.component.ts | 21 ++--- .../shared/references-editor.component.ts | 21 ++--- .../pages/dashboard-page.component.ts | 2 +- .../pages/schema/schema-page.component.ts | 24 ++--- .../pages/schemas/schema-form.component.ts | 3 +- .../pages/webhook-events-page.component.ts | 2 +- .../webhooks/pages/webhook.component.ts | 2 +- .../webhooks/pages/webhooks-page.component.ts | 2 +- .../angular/autocomplete.component.ts | 19 ++-- .../angular/confirm-click.directive.ts | 12 ++- .../app/framework/angular/copy.directive.ts | 3 +- .../angular/date-time-editor.component.ts | 20 ++-- .../angular/dialog-renderer.component.ts | 2 +- .../framework/angular/dropdown.component.ts | 13 ++- .../angular/geolocation-editor.component.ts | 31 ++++--- .../framework/angular/http-extensions-impl.ts | 2 +- .../angular/indeterminate-value.directive.ts | 18 ++-- .../angular/jscript-editor.component.ts | 30 +++--- .../angular/json-editor.component.ts | 35 +++---- .../angular/lowercase-input.directive.ts | 16 ++-- .../angular/markdown-editor.component.ts | 24 ++--- .../framework/angular/modal-view.directive.ts | 4 +- .../angular/panel-container.directive.ts | 2 +- .../angular/rich-editor.component.ts | 24 ++--- .../app/framework/angular/router-utils.ts | 4 +- .../app/framework/angular/slider.component.ts | 22 ++--- .../app/framework/angular/stars.component.ts | 37 ++++---- .../framework/angular/tag-editor.component.ts | 56 +++++++---- .../app/framework/angular/toggle.component.ts | 21 +++-- .../app/framework/angular/validators.ts | 39 ++++---- src/Squidex/app/framework/declarations.ts | 1 + .../framework/services/local-cache.service.ts | 2 +- src/Squidex/app/framework/utils/types.spec.ts | 92 +++++++++++++++++++ src/Squidex/app/framework/utils/types.ts | 70 ++++++++++++++ .../app/shared/components/asset.component.ts | 6 +- .../guards/resolve-app-languages.guard.ts | 4 +- .../shared/guards/resolve-content.guard.ts | 4 +- .../app/shared/guards/resolve-user.guard.ts | 4 +- .../interceptors/auth.interceptor.spec.ts | 2 +- .../shared/interceptors/auth.interceptor.ts | 2 +- .../shared/services/app-clients.service.ts | 6 +- .../services/app-contributors.service.ts | 4 +- .../shared/services/app-languages.service.ts | 6 +- .../shared/services/assets.service.spec.ts | 19 ++-- .../app/shared/services/assets.service.ts | 23 ++--- .../app/shared/services/auth.service.ts | 18 ++-- .../shared/services/contents.service.spec.ts | 16 ++-- .../app/shared/services/contents.service.ts | 14 +-- .../app/shared/services/plans.service.ts | 2 +- .../shared/services/schemas.service.spec.ts | 32 +++---- .../app/shared/services/schemas.service.ts | 38 ++++---- .../app/shared/services/ui.service.spec.ts | 3 +- .../shared/services/webhooks.service.spec.ts | 4 +- .../app/shared/services/webhooks.service.ts | 10 +- .../pages/internal/profile-menu.component.ts | 6 +- src/Squidex/tsconfig.json | 2 +- 58 files changed, 554 insertions(+), 353 deletions(-) create mode 100644 src/Squidex/app/framework/utils/types.spec.ts create mode 100644 src/Squidex/app/framework/utils/types.ts 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 b181af1f4..8645ebcfa 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 @@ -139,7 +139,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone this.appNameOnce() .switchMap(app => this.contentsService.putContent(app, this.schema.name, this.contentId!, requestDto, this.version)) .subscribe(dto => { - this.content = this.content.update(dto, this.authService.user.token); + this.content = this.content.update(dto, this.authService.user!.token); this.emitContentUpdated(this.content); this.notifyInfo('Content saved successfully.'); 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 998747c61..f914ca7da 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 @@ -123,7 +123,7 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy this.appNameOnce() .switchMap(app => this.contentsService.publishContent(app, this.schema.name, content.id, content.version)) .subscribe(() => { - this.contentItems = this.contentItems.replaceBy('id', content.publish(this.authService.user.token)); + this.contentItems = this.contentItems.replaceBy('id', content.publish(this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -133,7 +133,7 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy this.appNameOnce() .switchMap(app => this.contentsService.unpublishContent(app, this.schema.name, content.id, content.version)) .subscribe(() => { - this.contentItems = this.contentItems.replaceBy('id', content.unpublish(this.authService.user.token)); + this.contentItems = this.contentItems.replaceBy('id', content.unpublish(this.authService.user!.token)); }, error => { this.notifyError(error); }); 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 e23fbd2ba..31e441741 100644 --- a/src/Squidex/app/features/content/shared/assets-editor.component.ts +++ b/src/Squidex/app/features/content/shared/assets-editor.component.ts @@ -19,11 +19,10 @@ import { AssetUpdated, DialogService, ImmutableArray, - MessageBus + MessageBus, + Types } from 'shared'; -const NOOP = () => { /* NOOP */ }; - export const SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AssetsEditorComponent), multi: true }; @@ -36,8 +35,8 @@ export const SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR: any = { }) export class AssetsEditorComponent extends AppComponentBase implements ControlValueAccessor, OnDestroy, OnInit { private assetUpdatedSubscription: Subscription; - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; public newAssets = ImmutableArray.empty(); public oldAssets = ImmutableArray.empty(); @@ -65,10 +64,10 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa this.assetUpdatedSubscription.unsubscribe(); } - public writeValue(value: any) { + public writeValue(value: string[]) { this.oldAssets = ImmutableArray.empty(); - if (value && value.length > 0) { + if (Types.isArrayOfString(value) && value.length > 0) { const assetIds: string[] = value; this.appNameOnce() @@ -84,11 +83,11 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public addFiles(files: FileList) { @@ -143,7 +142,7 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa ids = null; } - this.touchedCallback(); - this.changeCallback(ids); + this.onTouched(); + this.onChange(ids); } } \ No newline at end of file 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 59b4e01d4..ded63f413 100644 --- a/src/Squidex/app/features/content/shared/references-editor.component.ts +++ b/src/Squidex/app/features/content/shared/references-editor.component.ts @@ -19,11 +19,10 @@ import { FieldDto, ImmutableArray, SchemaDetailsDto, - SchemasService + SchemasService, + Types } from 'shared'; -const NOOP = () => { /* NOOP */ }; - export const SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferencesEditorComponent), multi: true }; @@ -35,8 +34,8 @@ export const SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class ReferencesEditorComponent extends AppComponentBase implements ControlValueAccessor, OnInit { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; @Input() public schemaId: string; @@ -73,10 +72,10 @@ export class ReferencesEditorComponent extends AppComponentBase implements Contr }); } - public writeValue(value: any) { + public writeValue(value: string[]) { this.contentItems = ImmutableArray.empty(); - if (value && value.length > 0) { + if (Types.isArrayOfString(value) && value.length > 0) { const contentIds: string[] = value; this.appNameOnce() @@ -92,11 +91,11 @@ export class ReferencesEditorComponent extends AppComponentBase implements Contr } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public canDrop() { @@ -138,8 +137,8 @@ export class ReferencesEditorComponent extends AppComponentBase implements Contr ids = null; } - this.touchedCallback(); - this.changeCallback(ids); + this.onTouched(); + this.onChange(ids); } private loadFields() { diff --git a/src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts b/src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts index 73d5ea2ca..c3ec3adad 100644 --- a/src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts +++ b/src/Squidex/app/features/dashboard/pages/dashboard-page.component.ts @@ -146,7 +146,7 @@ export class DashboardPageComponent extends AppComponentBase implements OnInit { }; }); - this.profileDisplayName = this.authService.user.displayName; + this.profileDisplayName = this.authService.user!.displayName; } public showForum() { 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 543ddc443..f38b8cc3f 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 @@ -110,7 +110,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.publishSchema(app, this.schema.name, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.publish(this.authService.user.token)); + this.updateSchema(this.schema.publish(this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -120,7 +120,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.unpublishSchema(app, this.schema.name, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.unpublish(this.authService.user.token)); + this.updateSchema(this.schema.unpublish(this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -130,7 +130,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.enableField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.updateField(field.enable(), this.authService.user.token)); + this.updateSchema(this.schema.updateField(field.enable(), this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -140,7 +140,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.disableField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.updateField(field.disable(), this.authService.user.token)); + this.updateSchema(this.schema.updateField(field.disable(), this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -150,7 +150,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.lockField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.updateField(field.lock(), this.authService.user.token)); + this.updateSchema(this.schema.updateField(field.lock(), this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -160,7 +160,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.hideField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.updateField(field.hide(), this.authService.user.token)); + this.updateSchema(this.schema.updateField(field.hide(), this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -170,7 +170,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.deleteField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.removeField(field, this.authService.user.token)); + this.updateSchema(this.schema.removeField(field, this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -180,7 +180,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.putFieldOrdering(app, this.schema.name, fields.map(t => t.fieldId), this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.replaceFields(fields, this.authService.user.token)); + this.updateSchema(this.schema.replaceFields(fields, this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -192,7 +192,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.putField(app, this.schema.name, field.fieldId, requestDto, this.schema.version)).retry(2) .subscribe(() => { - this.updateSchema(this.schema.updateField(field, this.authService.user.token)); + this.updateSchema(this.schema.updateField(field, this.authService.user!.token)); }, error => { this.notifyError(error); }); @@ -223,7 +223,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.schemasService.postField(app, this.schema.name, requestDto, this.schema.version)) .subscribe(dto => { - this.updateSchema(this.schema.addField(dto, this.authService.user.token)); + this.updateSchema(this.schema.addField(dto, this.authService.user!.token)); this.resetFieldForm(); }, error => { this.notifyError(error); @@ -237,13 +237,13 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit { } public onSchemaSaved(properties: SchemaPropertiesDto) { - this.updateSchema(this.schema.update(properties, this.authService.user.token)); + this.updateSchema(this.schema.update(properties, this.authService.user!.token)); this.editSchemaDialog.hide(); } public onSchemaScriptsSaved(scripts: UpdateSchemaScriptsDto) { - this.updateSchema(this.schema.configureScripts(scripts, this.authService.user.token)); + this.updateSchema(this.schema.configureScripts(scripts, this.authService.user!.token)); this.configureScriptsDialog.hide(); } diff --git a/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts b/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts index 6f903d98f..d51a15659 100644 --- a/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts +++ b/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts @@ -11,6 +11,7 @@ import { FormBuilder, Validators } from '@angular/forms'; import { ApiUrlConfig, AuthService, + DateTime, fadeAnimation, SchemaDetailsDto, SchemasService, @@ -89,7 +90,7 @@ export class SchemaFormComponent { const me = this.authService.user!.token; - this.schemas.postSchema(this.appName, requestDto, me, undefined, schemaVersion) + this.schemas.postSchema(this.appName, requestDto, me, DateTime.now(), schemaVersion) .subscribe(dto => { this.emitCreated(dto); this.resetCreateForm(); diff --git a/src/Squidex/app/features/webhooks/pages/webhook-events-page.component.ts b/src/Squidex/app/features/webhooks/pages/webhook-events-page.component.ts index a22dae47f..a5db09266 100644 --- a/src/Squidex/app/features/webhooks/pages/webhook-events-page.component.ts +++ b/src/Squidex/app/features/webhooks/pages/webhook-events-page.component.ts @@ -26,7 +26,7 @@ export class WebhookEventsPageComponent extends AppComponentBase implements OnIn public eventsItems = ImmutableArray.empty(); public eventsPager = new Pager(0); - public selectedEventId: string; + public selectedEventId: string | null = null; constructor(dialogs: DialogService, appsStore: AppsStoreService, private readonly webhooksService: WebhooksService diff --git a/src/Squidex/app/features/webhooks/pages/webhook.component.ts b/src/Squidex/app/features/webhooks/pages/webhook.component.ts index 34c51dbbe..ae8b3ac54 100644 --- a/src/Squidex/app/features/webhooks/pages/webhook.component.ts +++ b/src/Squidex/app/features/webhooks/pages/webhook.component.ts @@ -91,7 +91,7 @@ export class WebhookComponent implements OnInit { } else { return null; } - }).filter(w => !!w)).sortByStringAsc(x => x.schema.name); + }).filter(w => w !== null).map(w => w!)).sortByStringAsc(x => x.schema.name); this.schemasToAdd = ImmutableArray.of( diff --git a/src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts b/src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts index c7619bae6..83d653306 100644 --- a/src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts +++ b/src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts @@ -91,7 +91,7 @@ export class WebhooksPageComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.webhooksService.putWebhook(app, webhook.id, requestDto, webhook.version)) .subscribe(dto => { - this.webhooks = this.webhooks.replace(webhook, webhook.update(requestDto, this.authService.user.token)); + this.webhooks = this.webhooks.replace(webhook, webhook.update(requestDto, this.authService.user!.token)); this.notifyInfo('Webhook saved.'); }, error => { diff --git a/src/Squidex/app/framework/angular/autocomplete.component.ts b/src/Squidex/app/framework/angular/autocomplete.component.ts index 3742e6fac..e492a7f6b 100644 --- a/src/Squidex/app/framework/angular/autocomplete.component.ts +++ b/src/Squidex/app/framework/angular/autocomplete.component.ts @@ -17,7 +17,6 @@ const KEY_ENTER = 13; const KEY_ESCAPE = 27; const KEY_UP = 38; const KEY_DOWN = 40; -const NOOP = () => { /* NOOP */ }; export const SQX_AUTOCOMPLETE_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AutocompleteComponent), multi: true @@ -31,8 +30,8 @@ export const SQX_AUTOCOMPLETE_CONTROL_VALUE_ACCESSOR: any = { }) export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, OnInit { private subscription: Subscription; - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; @Input() public source: AutocompleteSource; @@ -57,7 +56,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O public writeValue(value: any) { if (!value) { - this.resetValue(); + this.resetForm(); } else { const item = this.items.find(i => i === value); @@ -79,11 +78,11 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public ngOnDestroy() { @@ -119,7 +118,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O this.down(); return false; case KEY_ESCAPE: - this.resetValue(); + this.resetForm(); this.reset(); return false; case KEY_ENTER: @@ -135,7 +134,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O public blur() { this.reset(); - this.touchedCallback(); + this.onTouched(); } public selectItem(selection: any | null = null) { @@ -154,7 +153,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O } else { this.queryInput.setValue(selection.toString(), { emitEvent: false }); } - this.changeCallback(selection); + this.onChange(selection); } finally { this.reset(); } @@ -181,7 +180,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O this.selectIndex(this.selectedIndex + 1); } - private resetValue() { + private resetForm() { this.queryInput.setValue(''); } diff --git a/src/Squidex/app/framework/angular/confirm-click.directive.ts b/src/Squidex/app/framework/angular/confirm-click.directive.ts index 74d9980b0..168453a8c 100644 --- a/src/Squidex/app/framework/angular/confirm-click.directive.ts +++ b/src/Squidex/app/framework/angular/confirm-click.directive.ts @@ -10,11 +10,13 @@ import { Directive, EventEmitter, HostListener, Input, OnDestroy, Output } from import { DialogService } from './../services/dialog.service'; class DelayEventEmitter extends EventEmitter { - private delayedNexts: any[] = []; + private delayedNexts: any[] | null = []; public delayEmit() { - for (let callback of this.delayedNexts) { - callback(); + if (this.delayedNexts) { + for (let callback of this.delayedNexts) { + callback(); + } } } @@ -23,7 +25,9 @@ class DelayEventEmitter extends EventEmitter { } public subscribe(generatorOrNext?: any, error?: any, complete?: any): any { - this.delayedNexts.push(generatorOrNext); + if (this.delayedNexts) { + this.delayedNexts.push(generatorOrNext); + } return super.subscribe(generatorOrNext, error, complete); } diff --git a/src/Squidex/app/framework/angular/copy.directive.ts b/src/Squidex/app/framework/angular/copy.directive.ts index 356b37bd5..8cf9f5a23 100644 --- a/src/Squidex/app/framework/angular/copy.directive.ts +++ b/src/Squidex/app/framework/angular/copy.directive.ts @@ -7,6 +7,7 @@ import { Directive, HostListener, Input } from '@angular/core'; +import { Types } from './../utils/types'; import { DialogService, Notification } from './../services/dialog.service'; @Directive({ @@ -48,7 +49,7 @@ export class CopyDirective { console.log('Copy failed'); } - if (currentFocus && typeof currentFocus.focus === 'function') { + if (currentFocus && Types.isFunction(currentFocus.focus)) { currentFocus.focus(); } diff --git a/src/Squidex/app/framework/angular/date-time-editor.component.ts b/src/Squidex/app/framework/angular/date-time-editor.component.ts index 85360418d..309b3bed4 100644 --- a/src/Squidex/app/framework/angular/date-time-editor.component.ts +++ b/src/Squidex/app/framework/angular/date-time-editor.component.ts @@ -12,8 +12,6 @@ import * as moment from 'moment'; let Pikaday = require('pikaday/pikaday'); -const NOOP = () => { /* NOOP */ }; - export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DateTimeEditorComponent), multi: true }; @@ -31,8 +29,8 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, private timeValue: any | null = null; private dateValue: any | null = null; private suppressEvents = false; - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; @Input() public mode: string; @@ -116,11 +114,11 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public ngAfterViewInit() { @@ -140,7 +138,7 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } public touched() { - this.touchedCallback(); + this.onTouched(); } public writeNow() { @@ -159,14 +157,14 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, this.dateValue = null; - this.changeCallback(null); - this.touchedCallback(); + this.onChange(null); + this.onTouched(); return false; } private updateValue() { - let result: string | null; + let result: string | null = null; if ((this.dateValue && !this.dateValue.isValid()) || (this.timeValue && !this.timeValue.isValid())) { result = 'Invalid DateTime'; @@ -184,7 +182,7 @@ export class DateTimeEditorComponent implements ControlValueAccessor, OnDestroy, } } - this.changeCallback(result); + this.onChange(result); } private updateControls() { diff --git a/src/Squidex/app/framework/angular/dialog-renderer.component.ts b/src/Squidex/app/framework/angular/dialog-renderer.component.ts index 32315a66b..12c1d549e 100644 --- a/src/Squidex/app/framework/angular/dialog-renderer.component.ts +++ b/src/Squidex/app/framework/angular/dialog-renderer.component.ts @@ -32,7 +32,7 @@ export class DialogRendererComponent implements OnDestroy, OnInit { private notificationsSubscription: Subscription; public dialogView = new ModalView(false, true); - public dialogRequest: DialogRequest; + public dialogRequest: DialogRequest | null = null; public notifications: Notification[] = []; diff --git a/src/Squidex/app/framework/angular/dropdown.component.ts b/src/Squidex/app/framework/angular/dropdown.component.ts index a4ec504a5..f7d0fc118 100644 --- a/src/Squidex/app/framework/angular/dropdown.component.ts +++ b/src/Squidex/app/framework/angular/dropdown.component.ts @@ -12,7 +12,6 @@ const KEY_ENTER = 13; const KEY_ESCAPE = 27; const KEY_UP = 38; const KEY_DOWN = 40; -const NOOP = () => { /* NOOP */ }; import { ModalView } from './../utils/modal-view'; @@ -27,8 +26,8 @@ export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR] }) export class DropdownComponent implements AfterContentInit, ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; @Input() public items: any[] = []; @@ -69,11 +68,11 @@ export class DropdownComponent implements AfterContentInit, ControlValueAccessor } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public onKeyDown(event: KeyboardEvent) { @@ -95,7 +94,7 @@ export class DropdownComponent implements AfterContentInit, ControlValueAccessor public open() { this.dropdown.show(); - this.touchedCallback(); + this.onTouched(); } public selectIndexAndClose(selectedIndex: number) { @@ -132,7 +131,7 @@ export class DropdownComponent implements AfterContentInit, ControlValueAccessor this.selectedIndex = selectedIndex; this.selectedItem = value; - this.changeCallback(value); + this.onChange(value); } } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/geolocation-editor.component.ts b/src/Squidex/app/framework/angular/geolocation-editor.component.ts index 00001813a..3cf44cbf3 100644 --- a/src/Squidex/app/framework/angular/geolocation-editor.component.ts +++ b/src/Squidex/app/framework/angular/geolocation-editor.component.ts @@ -8,17 +8,22 @@ import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Types } from './../utils/types'; + import { ResourceLoaderService } from './../services/resource-loader.service'; import { ValidatorsEx } from './validators'; -const NOOP = () => { /* NOOP */ }; - declare var L: any; export const SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => GeolocationEditorComponent), multi: true }; +interface Geolocation { + latitude: number; + longitude: number; +} + @Component({ selector: 'sqx-geolocation-editor', styleUrls: ['./geolocation-editor.component.scss'], @@ -26,11 +31,11 @@ export const SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class GeolocationEditorComponent implements ControlValueAccessor, AfterViewInit { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; private marker: any; private map: any; - private value: any; + private value: Geolocation | null = null; public get hasValue() { return !!this.value; @@ -59,8 +64,12 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi ) { } - public writeValue(value: any) { - this.value = value; + public writeValue(value: Geolocation) { + if (Types.isObject(value) && Types.isNumber(value.latitude) && Types.isNumber(value.longitude)) { + this.value = value; + } else { + this.value = null; + } if (this.marker) { this.updateMarker(true, false); @@ -102,11 +111,11 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public updateValueByInput() { @@ -201,8 +210,8 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi } if (fireEvent) { - this.changeCallback(this.value); - this.touchedCallback(); + this.onChange(this.value); + this.onTouched(); } } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/http-extensions-impl.ts b/src/Squidex/app/framework/angular/http-extensions-impl.ts index 2ff40ff46..ac444d80a 100644 --- a/src/Squidex/app/framework/angular/http-extensions-impl.ts +++ b/src/Squidex/app/framework/angular/http-extensions-impl.ts @@ -78,7 +78,7 @@ export module HTTP { } } - function handleVersion(httpRequest: Observable>, version: Version): Observable { + function handleVersion(httpRequest: Observable>, version?: Version): Observable { return httpRequest.do((response: HttpResponse) => { if (version && response.status.toString().indexOf('2') === 0 && response.headers) { const etag = response.headers.get('etag'); diff --git a/src/Squidex/app/framework/angular/indeterminate-value.directive.ts b/src/Squidex/app/framework/angular/indeterminate-value.directive.ts index 9e48bca7e..034bd5b79 100644 --- a/src/Squidex/app/framework/angular/indeterminate-value.directive.ts +++ b/src/Squidex/app/framework/angular/indeterminate-value.directive.ts @@ -8,7 +8,7 @@ import { Directive, forwardRef, ElementRef, HostListener, Renderer } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -const NOOP = () => { /* NOOP */ }; +import { Types } from './../utils/types'; export const SQX_INDETERMINATE_VALUE_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => IndeterminateValueDirective), multi: true @@ -19,8 +19,8 @@ export const SQX_INDETERMINATE_VALUE_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_INDETERMINATE_VALUE_CONTROL_VALUE_ACCESSOR] }) export class IndeterminateValueDirective implements ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; constructor( private readonly renderer: Renderer, @@ -30,16 +30,16 @@ export class IndeterminateValueDirective implements ControlValueAccessor { @HostListener('change', ['$event.target.value']) public onChange(value: any) { - this.changeCallback(value); + this.onChange(value); } @HostListener('blur') public onTouched() { - this.touchedCallback(); + this.onTouched(); } - public writeValue(value: any) { - if (value === undefined || value === null) { + public writeValue(value: boolean | number | undefined) { + if (!Types.isBoolean(value)) { this.renderer.setElementProperty(this.element.nativeElement, 'indeterminate', true); } else { this.renderer.setElementProperty(this.element.nativeElement, 'checked', value); @@ -51,10 +51,10 @@ export class IndeterminateValueDirective implements ControlValueAccessor { } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/jscript-editor.component.ts b/src/Squidex/app/framework/angular/jscript-editor.component.ts index be2f225b6..e2b7cc74e 100644 --- a/src/Squidex/app/framework/angular/jscript-editor.component.ts +++ b/src/Squidex/app/framework/angular/jscript-editor.component.ts @@ -9,12 +9,12 @@ import { AfterViewInit, Component, forwardRef, ElementRef, ViewChild } from '@an import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject } from 'rxjs'; +import { Types } from './../utils/types'; + import { ResourceLoaderService } from './../services/resource-loader.service'; declare var ace: any; -const NOOP = () => { /* NOOP */ }; - export const SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => JscriptEditorComponent), multi: true }; @@ -26,11 +26,11 @@ export const SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_JSCRIPT_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class JscriptEditorComponent implements ControlValueAccessor, AfterViewInit { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; private valueChanged = new Subject(); private aceEditor: any; - private oldValue: string; + private value: string; private isDisabled = false; @ViewChild('editor') @@ -41,11 +41,11 @@ export class JscriptEditorComponent implements ControlValueAccessor, AfterViewIn ) { } - public writeValue(value: any) { - this.oldValue = value; + public writeValue(value: string) { + this.value = Types.isString(value) ? value : ''; if (this.aceEditor) { - this.setValue(value); + this.setValue(this.value); } } @@ -58,11 +58,11 @@ export class JscriptEditorComponent implements ControlValueAccessor, AfterViewIn } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public ngAfterViewInit() { @@ -78,11 +78,11 @@ export class JscriptEditorComponent implements ControlValueAccessor, AfterViewIn this.aceEditor.setReadOnly(this.isDisabled); this.aceEditor.setFontSize(14); - this.setValue(this.oldValue); + this.setValue(this.value); this.aceEditor.on('blur', () => { this.changeValue(); - this.touchedCallback(); + this.onTouched(); }); this.aceEditor.on('change', () => { @@ -94,11 +94,11 @@ export class JscriptEditorComponent implements ControlValueAccessor, AfterViewIn private changeValue() { const newValue = this.aceEditor.getValue(); - if (this.oldValue !== newValue) { - this.changeCallback(newValue); + if (this.value !== newValue) { + this.onChange(newValue); } - this.oldValue = newValue; + this.value = newValue; } private setValue(value: string) { diff --git a/src/Squidex/app/framework/angular/json-editor.component.ts b/src/Squidex/app/framework/angular/json-editor.component.ts index 281d7d863..90beb4b27 100644 --- a/src/Squidex/app/framework/angular/json-editor.component.ts +++ b/src/Squidex/app/framework/angular/json-editor.component.ts @@ -13,8 +13,6 @@ import { ResourceLoaderService } from './../services/resource-loader.service'; declare var ace: any; -const NOOP = () => { /* NOOP */ }; - export const SQX_JSON_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => JsonEditorComponent), multi: true }; @@ -26,12 +24,12 @@ export const SQX_JSON_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_JSON_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; private valueChanged = new Subject(); private aceEditor: any; - private oldValue: any; - private oldValueString: string; + private value: any; + private valueString: string; private isDisabled = false; @ViewChild('editor') @@ -43,8 +41,13 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit } public writeValue(value: any) { - this.oldValue = value; - this.oldValueString = JSON.stringify(value); + this.value = value; + + try { + this.valueString = JSON.stringify(value); + } catch (e) { + this.valueString = ''; + } if (this.aceEditor) { this.setValue(value); @@ -60,11 +63,11 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public ngAfterViewInit() { @@ -80,11 +83,11 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit this.aceEditor.setReadOnly(this.isDisabled); this.aceEditor.setFontSize(14); - this.setValue(this.oldValue); + this.setValue(this.value); this.aceEditor.on('blur', () => { this.changeValue(); - this.touchedCallback(); + this.onTouched(); }); this.aceEditor.on('change', () => { @@ -108,12 +111,12 @@ export class JsonEditorComponent implements ControlValueAccessor, AfterViewInit const newValueString = JSON.stringify(newValue); - if (this.oldValueString !== newValueString) { - this.changeCallback(newValue); + if (this.valueString !== newValueString) { + this.onChange(newValue); } - this.oldValue = newValue; - this.oldValueString = newValueString; + this.value = newValue; + this.valueString = newValueString; } private setValue(value: any) { diff --git a/src/Squidex/app/framework/angular/lowercase-input.directive.ts b/src/Squidex/app/framework/angular/lowercase-input.directive.ts index 69b40315a..66d61338d 100644 --- a/src/Squidex/app/framework/angular/lowercase-input.directive.ts +++ b/src/Squidex/app/framework/angular/lowercase-input.directive.ts @@ -8,8 +8,6 @@ import { Directive, forwardRef, ElementRef, HostListener, Renderer } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -const NOOP = () => { /* NOOP */ }; - export const SQX_LOWERCASE_INPUT_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => LowerCaseInputDirective), multi: true }; @@ -19,8 +17,8 @@ export const SQX_LOWERCASE_INPUT_VALUE_ACCESSOR: any = { providers: [SQX_LOWERCASE_INPUT_VALUE_ACCESSOR] }) export class LowerCaseInputDirective implements ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; constructor( private readonly element: ElementRef, @@ -33,16 +31,16 @@ export class LowerCaseInputDirective implements ControlValueAccessor { const normalizedValue = (value == null ? '' : value.toString()).toLowerCase(); this.renderer.setElementProperty(this.element.nativeElement, 'value', normalizedValue); - this.changeCallback(normalizedValue); + this.onChange(normalizedValue); } @HostListener('blur') public onTouched() { - this.touchedCallback(); + this.onTouched(); } public writeValue(value: any) { - const normalizedValue = (value == null ? '' : value.toString()).toLowerCase(); + const normalizedValue = value ? '' : value.toString().toLowerCase(); this.renderer.setElementProperty(this.element.nativeElement, 'value', normalizedValue); } @@ -52,10 +50,10 @@ export class LowerCaseInputDirective implements ControlValueAccessor { } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/markdown-editor.component.ts b/src/Squidex/app/framework/angular/markdown-editor.component.ts index b06f9494b..9716ef10f 100644 --- a/src/Squidex/app/framework/angular/markdown-editor.component.ts +++ b/src/Squidex/app/framework/angular/markdown-editor.component.ts @@ -8,12 +8,12 @@ import { AfterViewInit, Component, forwardRef, ElementRef, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Types } from './../utils/types'; + import { ResourceLoaderService } from './../services/resource-loader.service'; declare var SimpleMDE: any; -const NOOP = () => { /* NOOP */ }; - export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true }; @@ -25,10 +25,10 @@ export const SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_MARKDOWN_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class MarkdownEditorComponent implements ControlValueAccessor, AfterViewInit { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; private simplemde: any; - private value: any; + private value: string; private isDisabled = false; @ViewChild('editor') @@ -48,11 +48,11 @@ export class MarkdownEditorComponent implements ControlValueAccessor, AfterViewI this.resourceLoader.loadStyle('https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css'); } - public writeValue(value: any) { - this.value = value; + public writeValue(value: string) { + this.value = Types.isString(value) ? value : ''; if (this.simplemde) { - this.simplemde.value(this.value || ''); + this.simplemde.value(this.value); } } @@ -65,11 +65,11 @@ export class MarkdownEditorComponent implements ControlValueAccessor, AfterViewI } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public ngAfterViewInit() { @@ -84,12 +84,12 @@ export class MarkdownEditorComponent implements ControlValueAccessor, AfterViewI if (this.value !== value) { this.value = value; - this.changeCallback(value); + this.onChange(value); } }); this.simplemde.codemirror.on('blur', () => { - this.touchedCallback(); + this.onTouched(); }); this.simplemde.codemirror.on('refresh', () => { diff --git a/src/Squidex/app/framework/angular/modal-view.directive.ts b/src/Squidex/app/framework/angular/modal-view.directive.ts index 3ff9afcdd..0082b4603 100644 --- a/src/Squidex/app/framework/angular/modal-view.directive.ts +++ b/src/Squidex/app/framework/angular/modal-view.directive.ts @@ -16,8 +16,8 @@ import { RootViewService } from './../services/root-view.service'; selector: '[sqxModalView]' }) export class ModalViewDirective implements OnChanges, OnDestroy { - private subscription: Subscription | null; - private clickHandler: Function | null; + private subscription: Subscription | null = null; + private clickHandler: Function | null = null; private renderedView: EmbeddedViewRef | null = null; @Input('sqxModalView') diff --git a/src/Squidex/app/framework/angular/panel-container.directive.ts b/src/Squidex/app/framework/angular/panel-container.directive.ts index b96e2912d..1a5b983bb 100644 --- a/src/Squidex/app/framework/angular/panel-container.directive.ts +++ b/src/Squidex/app/framework/angular/panel-container.directive.ts @@ -49,7 +49,7 @@ export class PanelContainerDirective implements AfterViewInit, OnDestroy { } public invalidate(params?: { force: boolean, resize: boolean }) { - this.isInit = this.isInit || (params && params.force); + this.isInit = this.isInit || (params && params.force) === true; if (!this.isInit) { return; diff --git a/src/Squidex/app/framework/angular/rich-editor.component.ts b/src/Squidex/app/framework/angular/rich-editor.component.ts index d43668144..c690ed43a 100644 --- a/src/Squidex/app/framework/angular/rich-editor.component.ts +++ b/src/Squidex/app/framework/angular/rich-editor.component.ts @@ -8,12 +8,12 @@ import { AfterViewInit, Component, forwardRef, ElementRef, OnDestroy, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Types } from './../utils/types'; + import { ResourceLoaderService } from './../services/resource-loader.service'; declare var tinymce: any; -const NOOP = () => { /* NOOP */ }; - export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RichEditorComponent), multi: true }; @@ -25,10 +25,10 @@ export const SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_RICH_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, OnDestroy { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; private tinyEditor: any; - private value: any; + private value: string; private isDisabled = false; @ViewChild('editor') @@ -39,11 +39,11 @@ export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, ) { } - public writeValue(value: any) { - this.value = value; + public writeValue(value: string) { + this.value = Types.isString(value) ? value : ''; if (this.tinyEditor) { - this.tinyEditor.setContent(value || ''); + this.tinyEditor.setContent(this.value); } } @@ -56,11 +56,11 @@ export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public ngAfterViewInit() { @@ -78,12 +78,12 @@ export class RichEditorComponent implements ControlValueAccessor, AfterViewInit, if (this.value !== value) { this.value = value; - self.changeCallback(value); + self.onChange(value); } }); self.tinyEditor.on('blur', () => { - self.touchedCallback(); + self.onTouched(); }); setTimeout(() => { diff --git a/src/Squidex/app/framework/angular/router-utils.ts b/src/Squidex/app/framework/angular/router-utils.ts index 82fb59638..14563787f 100644 --- a/src/Squidex/app/framework/angular/router-utils.ts +++ b/src/Squidex/app/framework/angular/router-utils.ts @@ -8,7 +8,7 @@ import { ActivatedRoute, ActivatedRouteSnapshot, Data, Params } from '@angular/router'; export function allData(value: ActivatedRouteSnapshot | ActivatedRoute): Data { - let snapshot: ActivatedRouteSnapshot = value['snapshot'] || value; + let snapshot: ActivatedRouteSnapshot | null = value['snapshot'] || value; const result: { [key: string]: any } = { }; @@ -25,7 +25,7 @@ export function allData(value: ActivatedRouteSnapshot | ActivatedRoute): Data { return result; } export function allParams(value: ActivatedRouteSnapshot | ActivatedRoute): Params { - let snapshot: ActivatedRouteSnapshot = value['snapshot'] || value; + let snapshot: ActivatedRouteSnapshot | null = value['snapshot'] || value; const result: { [key: string]: any } = { }; diff --git a/src/Squidex/app/framework/angular/slider.component.ts b/src/Squidex/app/framework/angular/slider.component.ts index f429e385f..2fe82b581 100644 --- a/src/Squidex/app/framework/angular/slider.component.ts +++ b/src/Squidex/app/framework/angular/slider.component.ts @@ -8,7 +8,7 @@ import { Component, ElementRef, forwardRef, Input, Renderer, ViewChild } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -const NOOP = () => { /* NOOP */ }; +import { Types } from './../utils/types'; export const SQX_SLIDER_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SliderComponent), multi: true @@ -21,10 +21,10 @@ export const SQX_SLIDER_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_SLIDER_CONTROL_VALUE_ACCESSOR] }) export class SliderComponent implements ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; - private mouseMoveSubscription: Function | null; - private mouseUpSubscription: Function | null; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; + private mouseMoveSubscription: Function | null = null; + private mouseUpSubscription: Function | null = null; private centerStartOffset = 0; private startValue: number; private lastValue: number; @@ -50,8 +50,8 @@ export class SliderComponent implements ControlValueAccessor { constructor(private readonly renderer: Renderer) { } - public writeValue(value: any) { - this.lastValue = this.value = value; + public writeValue(value: number) { + this.lastValue = this.value = Types.isNumber(value) ? value : 0; this.updateThumbPosition(); } @@ -61,11 +61,11 @@ export class SliderComponent implements ControlValueAccessor { } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public onBarMouseClick(event: MouseEvent): boolean { @@ -157,14 +157,14 @@ export class SliderComponent implements ControlValueAccessor { } private updateTouched() { - this.touchedCallback(); + this.onTouched(); } private updateValue() { if (this.lastValue !== this.value) { this.lastValue = this.value; - this.changeCallback(this.value); + this.onChange(this.value); } } diff --git a/src/Squidex/app/framework/angular/stars.component.ts b/src/Squidex/app/framework/angular/stars.component.ts index 9b751b494..6afa84d37 100644 --- a/src/Squidex/app/framework/angular/stars.component.ts +++ b/src/Squidex/app/framework/angular/stars.component.ts @@ -8,7 +8,7 @@ import { Component, forwardRef, Input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -const NOOP = () => { /* NOOP */ }; +import { Types } from './../utils/types'; export const SQX_STARS_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StarsComponent), multi: true @@ -21,19 +21,15 @@ export const SQX_STARS_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_STARS_CONTROL_VALUE_ACCESSOR] }) export class StarsComponent implements ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; private maximumStarsValue = 5; @Input() - public set maximumStars(value: any) { - value = value || 5; + public set maximumStars(value: number) { + const maxStars: number = Types.isNumber(value) ? value : 5; - if (!(typeof value === 'number')) { - value = 5; - } - - if (this.maximumStarsValue !== value) { + if (this.maximumStarsValue !== maxStars) { this.maximumStarsValue = value; this.starsArray = []; @@ -55,8 +51,13 @@ export class StarsComponent implements ControlValueAccessor { public value: number | null = 1; - public writeValue(value: any) { - this.value = this.stars = value; + public writeValue(value: number | null | undefined) { + if (Types.isNumber(value)) { + this.value = this.stars = value!; + } else { + this.value = null; + this.stars = 0; + } } public setDisabledState(isDisabled: boolean): void { @@ -64,11 +65,11 @@ export class StarsComponent implements ControlValueAccessor { } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public setPreview(value: number) { @@ -96,8 +97,8 @@ export class StarsComponent implements ControlValueAccessor { this.value = null; this.stars = 0; - this.changeCallback(this.value); - this.touchedCallback(); + this.onChange(this.value); + this.onTouched(); } return false; @@ -111,8 +112,8 @@ export class StarsComponent implements ControlValueAccessor { if (this.value !== value) { this.value = this.stars = value; - this.changeCallback(this.value); - this.touchedCallback(); + this.onChange(this.value); + this.onTouched(); } return false; diff --git a/src/Squidex/app/framework/angular/tag-editor.component.ts b/src/Squidex/app/framework/angular/tag-editor.component.ts index 45efc61fd..e379b1947 100644 --- a/src/Squidex/app/framework/angular/tag-editor.component.ts +++ b/src/Squidex/app/framework/angular/tag-editor.component.ts @@ -8,40 +8,54 @@ import { Component, forwardRef, Input } from '@angular/core'; import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Types } from './../utils/types'; + const KEY_ENTER = 13; -const NOOP = () => { /* NOOP */ }; export interface Converter { convert(input: string): any; - isValid(input: string): boolean; + isValidInput(input: string): boolean; + isValidValue(value: any): boolean; } export class IntConverter implements Converter { - public isValid(input: string): boolean { + public isValidInput(input: string): boolean { return !!parseInt(input, 10) || input === '0'; } + public isValidValue(value: any): boolean { + return Types.isNumber(value); + } + public convert(input: string): any { return parseInt(input, 10) || 0; } } export class FloatConverter implements Converter { - public isValid(input: string): boolean { + public isValidInput(input: string): boolean { return !!parseFloat(input) || input === '0'; } + public isValidValue(value: any): boolean { + return Types.isNumber(value); + } + public convert(input: string): any { return parseFloat(input) || 0; } } -export class NoopConverter implements Converter { - public isValid(input: string): boolean { +export class StringConverter implements Converter { + public isValidInput(input: string): boolean { return input.trim().length > 0; } + public isValidValue(value: any): boolean { + return Types.isString(value); + } + public convert(input: string): any { return input.trim(); } @@ -58,11 +72,11 @@ export const SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR] }) export class TagEditorComponent implements ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; @Input() - public converter: Converter = new NoopConverter(); + public converter: Converter = new StringConverter(); @Input() public useDefaultValue = true; @@ -74,10 +88,10 @@ export class TagEditorComponent implements ControlValueAccessor { public addInput = new FormControl(); - public writeValue(value: any) { - this.addInput.setValue(''); + public writeValue(value: any[]) { + this.resetForm(); - if (Array.isArray(value)) { + if (this.converter && Types.isArrayOf(value, v => this.converter.isValidValue(v))) { this.items = value; } else { this.items = []; @@ -93,11 +107,11 @@ export class TagEditorComponent implements ControlValueAccessor { } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public remove(index: number) { @@ -105,18 +119,22 @@ export class TagEditorComponent implements ControlValueAccessor { } public markTouched() { - this.touchedCallback(); + this.onTouched(); + } + + private resetForm() { + this.addInput.reset(); } public onKeyDown(event: KeyboardEvent) { if (event.keyCode === KEY_ENTER) { const value = this.addInput.value; - if (this.converter.isValid(value)) { + if (this.converter.isValidInput(value)) { const converted = this.converter.convert(value); this.updateItems([...this.items, converted]); - this.addInput.reset(); + this.resetForm(); return false; } } @@ -128,9 +146,9 @@ export class TagEditorComponent implements ControlValueAccessor { this.items = items; if (items.length === 0 && this.useDefaultValue) { - this.changeCallback(undefined); + this.onChange(undefined); } else { - this.changeCallback(this.items); + this.onChange(this.items); } } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/toggle.component.ts b/src/Squidex/app/framework/angular/toggle.component.ts index 8073ecf9d..c45705ce3 100644 --- a/src/Squidex/app/framework/angular/toggle.component.ts +++ b/src/Squidex/app/framework/angular/toggle.component.ts @@ -8,7 +8,7 @@ import { Component, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -const NOOP = () => { /* NOOP */ }; +import { Types } from './../utils/types'; export const SQX_TOGGLE_CONTROL_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ToggleComponent), multi: true @@ -21,14 +21,14 @@ export const SQX_TOGGLE_CONTROL_VALUE_ACCESSOR: any = { providers: [SQX_TOGGLE_CONTROL_VALUE_ACCESSOR] }) export class ToggleComponent implements ControlValueAccessor { - private changeCallback: (value: any) => void = NOOP; - private touchedCallback: () => void = NOOP; + private onChange = (v: any) => { /* NOOP */ }; + private onTouched = () => { /* NOOP */ }; - public isChecked: boolean | undefined = undefined; + public isChecked: boolean | null = null; public isDisabled = false; - public writeValue(value: any) { - this.isChecked = value; + public writeValue(value: boolean | null | undefined) { + this.isChecked = Types.isBoolean(value) ? value! : null; } public setDisabledState(isDisabled: boolean): void { @@ -36,20 +36,21 @@ export class ToggleComponent implements ControlValueAccessor { } public registerOnChange(fn: any) { - this.changeCallback = fn; + this.onChange = fn; } public registerOnTouched(fn: any) { - this.touchedCallback = fn; + this.onTouched = fn; } public changeState() { if (this.isDisabled) { return; } + this.isChecked = !(this.isChecked === true); - this.changeCallback(this.isChecked); - this.touchedCallback(); + this.onChange(this.isChecked); + this.onTouched(); } } \ No newline at end of file diff --git a/src/Squidex/app/framework/angular/validators.ts b/src/Squidex/app/framework/angular/validators.ts index 7ee6ee864..0e8176155 100644 --- a/src/Squidex/app/framework/angular/validators.ts +++ b/src/Squidex/app/framework/angular/validators.ts @@ -12,6 +12,7 @@ import { } from '@angular/forms'; import { DateTime } from './../utils/date-time'; +import { Types } from './../utils/types'; export module ValidatorsEx { export function pattern(regex: string | RegExp, message?: string): ValidatorFn { @@ -30,7 +31,7 @@ export module ValidatorsEx { regeExp = regex; } - return (control: AbstractControl): { [key: string]: any } => { + return (control: AbstractControl) => { const n: string = control.value; if (n == null || n.length === 0) { @@ -49,16 +50,16 @@ export module ValidatorsEx { }; } - export function match(otherControlName: string, message: string) { - let otherControl: AbstractControl = null; + export function match(otherControlName: string, message: string): ValidatorFn { + let otherControl: AbstractControl | null = null; - return (control: AbstractControl): { [key: string]: any } => { + return (control: AbstractControl) => { if (!control.parent) { return null; } if (otherControl === null) { - otherControl = control.parent.get(otherControlName) || undefined; + otherControl = control.parent.get(otherControlName); if (!otherControl) { throw new Error('matchValidator(): other control is not found in parent group'); @@ -77,8 +78,8 @@ export module ValidatorsEx { }; } - export function validDateTime() { - return (control: AbstractControl): { [key: string]: any } => { + export function validDateTime(): ValidatorFn { + return (control: AbstractControl) => { const v: string = control.value; if (v) { @@ -93,32 +94,32 @@ export module ValidatorsEx { }; } - export function between(minValue?: number, maxValue?: number) { + export function between(minValue?: number, maxValue?: number): ValidatorFn { if (!minValue || !maxValue) { return Validators.nullValidator; } - return (control: AbstractControl): { [key: string]: any } => { - const n: number = control.value; + return (control: AbstractControl) => { + const value: number = control.value; - if (typeof n !== 'number') { + if (!Types.isNumber(value)) { return { validnumber: false }; - } else if (minValue && n < minValue) { - return { minvalue: { minValue, actualValue: n } }; - } else if (maxValue && n > maxValue) { - return { maxvalue: { maxValue, actualValue: n } }; + } else if (minValue && value < minValue) { + return { minvalue: { minValue, actualValue: value } }; + } else if (maxValue && value > maxValue) { + return { maxvalue: { maxValue, actualValue: value } }; } return null; }; } - export function validValues(values: T[]) { + export function validValues(values: T[]): ValidatorFn { if (!values) { return Validators.nullValidator; } - return (control: AbstractControl): { [key: string]: any } => { + return (control: AbstractControl) => { const n: T = control.value; if (values.indexOf(n) < 0) { @@ -129,8 +130,8 @@ export module ValidatorsEx { }; } - export function noop() { - return (control: AbstractControl): { [key: string]: any } => { + export function noop(): ValidatorFn { + return (control: AbstractControl) => { return null; }; } diff --git a/src/Squidex/app/framework/declarations.ts b/src/Squidex/app/framework/declarations.ts index c17562594..e150b3563 100644 --- a/src/Squidex/app/framework/declarations.ts +++ b/src/Squidex/app/framework/declarations.ts @@ -71,4 +71,5 @@ export * from './utils/math-helper'; export * from './utils/modal-view'; export * from './utils/pager'; export * from './utils/string-helper'; +export * from './utils/types'; export * from './utils/version'; \ No newline at end of file diff --git a/src/Squidex/app/framework/services/local-cache.service.ts b/src/Squidex/app/framework/services/local-cache.service.ts index 1512fe0e8..ba81cc4f5 100644 --- a/src/Squidex/app/framework/services/local-cache.service.ts +++ b/src/Squidex/app/framework/services/local-cache.service.ts @@ -31,7 +31,7 @@ export class LocalCacheService { } } - public get(key: string, now?: number): T { + public get(key: string, now?: number): T | undefined { const entry = this.entries[key]; if (entry) { diff --git a/src/Squidex/app/framework/utils/types.spec.ts b/src/Squidex/app/framework/utils/types.spec.ts new file mode 100644 index 000000000..6b471032a --- /dev/null +++ b/src/Squidex/app/framework/utils/types.spec.ts @@ -0,0 +1,92 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { Types } from './../'; + +describe('Types', () => { + it('should make string check', () => { + expect(Types.isString('')).toBeTruthy(); + expect(Types.isString('string')).toBeTruthy(); + + expect(Types.isString(false)).toBeFalsy(); + }); + + it('should make number check', () => { + expect(Types.isNumber(0)).toBeTruthy(); + expect(Types.isNumber(1)).toBeTruthy(); + + expect(Types.isNumber(NaN)).toBeFalsy(); + expect(Types.isNumber(Infinity)).toBeFalsy(); + expect(Types.isNumber(false)).toBeFalsy(); + }); + + it('should make boolean check', () => { + expect(Types.isBoolean(true)).toBeTruthy(); + expect(Types.isBoolean(false)).toBeTruthy(); + + expect(Types.isBoolean(0)).toBeFalsy(); + expect(Types.isBoolean(1)).toBeFalsy(); + }); + + it('should make number array check', () => { + expect(Types.isArrayOfNumber([])).toBeTruthy(); + expect(Types.isArrayOfNumber([0, 1])).toBeTruthy(); + + expect(Types.isArrayOfNumber(['0', 1])).toBeFalsy(); + }); + + it('should make string array check', () => { + expect(Types.isArrayOfString([])).toBeTruthy(); + expect(Types.isArrayOfString(['0', '1'])).toBeTruthy(); + + expect(Types.isArrayOfString(['0', 1])).toBeFalsy(); + }); + + it('should make array check', () => { + expect(Types.isArray([])).toBeTruthy(); + expect(Types.isArray([0])).toBeTruthy(); + + expect(Types.isArray({})).toBeFalsy(); + }); + + it('should make object check', () => { + expect(Types.isObject({})).toBeTruthy(); + expect(Types.isObject({ v: 1 })).toBeTruthy(); + + expect(Types.isObject([])).toBeFalsy(); + }); + + it('should make RegExp check', () => { + expect(Types.isRegExp(/[.*]/)).toBeTruthy(); + + expect(Types.isRegExp('/[.*]/')).toBeFalsy(); + }); + + it('should make Date check', () => { + expect(Types.isDate(new Date())).toBeTruthy(); + + expect(Types.isDate(new Date().getDate())).toBeFalsy(); + }); + + it('should make undefined check', () => { + expect(Types.isUndefined(undefined)).toBeTruthy(); + + expect(Types.isUndefined(null)).toBeFalsy(); + }); + + it('should make null check', () => { + expect(Types.isNull(null)).toBeTruthy(); + + expect(Types.isNull(undefined)).toBeFalsy(); + }); + + it('should make function check', () => { + expect(Types.isFunction(() => { /* NOOP */ })).toBeTruthy(); + + expect(Types.isFunction([])).toBeFalsy(); + }); +}); \ No newline at end of file diff --git a/src/Squidex/app/framework/utils/types.ts b/src/Squidex/app/framework/utils/types.ts new file mode 100644 index 000000000..d379b159f --- /dev/null +++ b/src/Squidex/app/framework/utils/types.ts @@ -0,0 +1,70 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +export module Types { + export function isString(value: any): boolean { + return typeof value === 'string' || value instanceof String; + } + + export function isNumber(value: any): boolean { + return typeof value === 'number' && isFinite(value); + } + + export function isArray(value: any): boolean { + return Array.isArray(value); + } + + export function isFunction(value: any): boolean { + return typeof value === 'function'; + } + + export function isObject(value: any): boolean { + return value && typeof value === 'object' && value.constructor === Object; + } + + export function isBoolean(value: any): boolean { + return typeof value === 'boolean'; + }; + + export function isNull(value: any): boolean { + return value === null; + } + + export function isUndefined(value: any): boolean { + return typeof value === 'undefined'; + } + + export function isRegExp(value: any): boolean { + return value && typeof value === 'object' && value.constructor === RegExp; + } + + export function isDate(value: any): boolean { + return value instanceof Date; + } + + export function isArrayOfNumber(value: any): boolean { + return isArrayOf(value, v => isNumber(v)); + } + + export function isArrayOfString(value: any): boolean { + return isArrayOf(value, v => isString(v)); + } + + export function isArrayOf(value: any, validator: (v: any) => boolean): boolean { + if (!Array.isArray(value)) { + return false; + } + + for (let v of value) { + if (!validator(v)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/Squidex/app/shared/components/asset.component.ts b/src/Squidex/app/shared/components/asset.component.ts index e901e9e46..4c37dfa6d 100644 --- a/src/Squidex/app/shared/components/asset.component.ts +++ b/src/Squidex/app/shared/components/asset.component.ts @@ -83,7 +83,7 @@ export class AssetComponent extends AppComponentBase implements OnInit { if (initFile) { this.appNameOnce() - .switchMap(app => this.assetsService.uploadFile(app, initFile, this.authService.user.token)) + .switchMap(app => this.assetsService.uploadFile(app, initFile, this.authService.user!.token)) .subscribe(dto => { if (dto instanceof AssetDto) { this.emitLoaded(dto); @@ -104,7 +104,7 @@ export class AssetComponent extends AppComponentBase implements OnInit { .switchMap(app => this.assetsService.replaceFile(app, this.asset.id, files[0], this.assetVersion)) .subscribe(dto => { if (dto instanceof AssetReplacedDto) { - this.updateAsset(this.asset.update(dto, this.authService.user.token), true); + this.updateAsset(this.asset.update(dto, this.authService.user!.token), true); } else { this.setProgress(dto); } @@ -126,7 +126,7 @@ export class AssetComponent extends AppComponentBase implements OnInit { this.appNameOnce() .switchMap(app => this.assetsService.putAsset(app, this.asset.id, requestDto, this.assetVersion)) .subscribe(() => { - this.updateAsset(this.asset.rename(requestDto.fileName, this.authService.user.token), true); + this.updateAsset(this.asset.rename(requestDto.fileName, this.authService.user!.token), true); this.resetRenameForm(); }, error => { this.notifyError(error); diff --git a/src/Squidex/app/shared/guards/resolve-app-languages.guard.ts b/src/Squidex/app/shared/guards/resolve-app-languages.guard.ts index 92128d956..afd763445 100644 --- a/src/Squidex/app/shared/guards/resolve-app-languages.guard.ts +++ b/src/Squidex/app/shared/guards/resolve-app-languages.guard.ts @@ -14,14 +14,14 @@ import { allParams } from 'framework'; import { AppLanguageDto, AppLanguagesService } from './../services/app-languages.service'; @Injectable() -export class ResolveAppLanguagesGuard implements Resolve { +export class ResolveAppLanguagesGuard implements Resolve { constructor( private readonly appLanguagesService: AppLanguagesService, private readonly router: Router ) { } - public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { const params = allParams(route); const appName = params['appName']; diff --git a/src/Squidex/app/shared/guards/resolve-content.guard.ts b/src/Squidex/app/shared/guards/resolve-content.guard.ts index d2e9efdfd..83a0dd4e3 100644 --- a/src/Squidex/app/shared/guards/resolve-content.guard.ts +++ b/src/Squidex/app/shared/guards/resolve-content.guard.ts @@ -14,14 +14,14 @@ import { allParams } from 'framework'; import { ContentDto, ContentsService } from './../services/contents.service'; @Injectable() -export class ResolveContentGuard implements Resolve { +export class ResolveContentGuard implements Resolve { constructor( private readonly contentsService: ContentsService, private readonly router: Router ) { } - public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { const params = allParams(route); const appName = params['appName']; diff --git a/src/Squidex/app/shared/guards/resolve-user.guard.ts b/src/Squidex/app/shared/guards/resolve-user.guard.ts index a65482ff2..82871eefd 100644 --- a/src/Squidex/app/shared/guards/resolve-user.guard.ts +++ b/src/Squidex/app/shared/guards/resolve-user.guard.ts @@ -14,14 +14,14 @@ import { allParams } from 'framework'; import { UserDto, UserManagementService } from './../services/users.service'; @Injectable() -export class ResolveUserGuard implements Resolve { +export class ResolveUserGuard implements Resolve { constructor( private readonly userManagementService: UserManagementService, private readonly router: Router ) { } - public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { const params = allParams(route); const userId = params['userId']; diff --git a/src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts b/src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts index 99515513d..d4b22c90c 100644 --- a/src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts +++ b/src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts @@ -18,7 +18,7 @@ import { } from './../'; describe('AuthInterceptor', () => { - let authService: IMock = null; + let authService: IMock; beforeEach(() => { authService = Mock.ofType(AuthService); diff --git a/src/Squidex/app/shared/interceptors/auth.interceptor.ts b/src/Squidex/app/shared/interceptors/auth.interceptor.ts index 6f7efeb38..860f142fb 100644 --- a/src/Squidex/app/shared/interceptors/auth.interceptor.ts +++ b/src/Squidex/app/shared/interceptors/auth.interceptor.ts @@ -32,7 +32,7 @@ export class AuthInterceptor implements HttpInterceptor { } } - private makeRequest(req: HttpRequest, next: HttpHandler, user: Profile, renew = false): Observable> { + private makeRequest(req: HttpRequest, next: HttpHandler, user: Profile | null, renew = false): Observable> { const token = user ? user.authToken : ''; const authReq = req.clone({ diff --git a/src/Squidex/app/shared/services/app-clients.service.ts b/src/Squidex/app/shared/services/app-clients.service.ts index 7e6d797a4..478581c55 100644 --- a/src/Squidex/app/shared/services/app-clients.service.ts +++ b/src/Squidex/app/shared/services/app-clients.service.ts @@ -84,7 +84,7 @@ export class AppClientsService { .pretifyError('Failed to load clients. Please reload.'); } - public postClient(appName: string, dto: CreateAppClientDto, version?: Version): Observable { + public postClient(appName: string, dto: CreateAppClientDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients`); return HTTP.postVersioned(this.http, url, dto, version) @@ -98,14 +98,14 @@ export class AppClientsService { .pretifyError('Failed to add client. Please reload.'); } - public updateClient(appName: string, id: string, dto: UpdateAppClientDto, version?: Version): Observable { + public updateClient(appName: string, id: string, dto: UpdateAppClientDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`); return HTTP.putVersioned(this.http, url, dto, version) .pretifyError('Failed to revoke client. Please reload.'); } - public deleteClient(appName: string, id: string, version?: Version): Observable { + public deleteClient(appName: string, id: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/clients/${id}`); return HTTP.deleteVersioned(this.http, url, version) diff --git a/src/Squidex/app/shared/services/app-contributors.service.ts b/src/Squidex/app/shared/services/app-contributors.service.ts index 46b2fbcdb..7ccc9f0d6 100644 --- a/src/Squidex/app/shared/services/app-contributors.service.ts +++ b/src/Squidex/app/shared/services/app-contributors.service.ts @@ -63,14 +63,14 @@ export class AppContributorsService { .pretifyError('Failed to load contributors. Please reload.'); } - public postContributor(appName: string, dto: AppContributorDto, version?: Version): Observable { + public postContributor(appName: string, dto: AppContributorDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors`); return HTTP.postVersioned(this.http, url, dto, version) .pretifyError('Failed to add contributors. Please reload.'); } - public deleteContributor(appName: string, contributorId: string, version?: Version): Observable { + public deleteContributor(appName: string, contributorId: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/contributors/${contributorId}`); return HTTP.deleteVersioned(this.http, url, version) diff --git a/src/Squidex/app/shared/services/app-languages.service.ts b/src/Squidex/app/shared/services/app-languages.service.ts index 811bb5ef1..3197562ff 100644 --- a/src/Squidex/app/shared/services/app-languages.service.ts +++ b/src/Squidex/app/shared/services/app-languages.service.ts @@ -75,7 +75,7 @@ export class AppLanguagesService { .pretifyError('Failed to load languages. Please reload.'); } - public postLanguages(appName: string, dto: AddAppLanguageDto, version?: Version): Observable { + public postLanguages(appName: string, dto: AddAppLanguageDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages`); return HTTP.postVersioned(this.http, url, dto, version) @@ -90,14 +90,14 @@ export class AppLanguagesService { .pretifyError('Failed to add language. Please reload.'); } - public updateLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version?: Version): Observable { + public updateLanguage(appName: string, languageCode: string, dto: UpdateAppLanguageDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`); return HTTP.putVersioned(this.http, url, dto, version) .pretifyError('Failed to change language. Please reload.'); } - public deleteLanguage(appName: string, languageCode: string, version?: Version): Observable { + public deleteLanguage(appName: string, languageCode: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/languages/${languageCode}`); return HTTP.deleteVersioned(this.http, url, version) diff --git a/src/Squidex/app/shared/services/assets.service.spec.ts b/src/Squidex/app/shared/services/assets.service.spec.ts index 014643cb8..8a23706b1 100644 --- a/src/Squidex/app/shared/services/assets.service.spec.ts +++ b/src/Squidex/app/shared/services/assets.service.spec.ts @@ -24,7 +24,7 @@ describe('AssetDto', () => { it('should update name property and user info when renaming', () => { const now = DateTime.now(); - const asset_1 = new AssetDto('1', 'other', 'other', DateTime.today(), DateTime.today(), 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, null); + const asset_1 = new AssetDto('1', 'other', 'other', DateTime.today(), DateTime.today(), 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, new Version('1')); const asset_2 = asset_1.rename('new-name.png', 'me', now); expect(asset_2.fileName).toEqual('new-name.png'); @@ -35,9 +35,9 @@ describe('AssetDto', () => { it('should update file properties when uploading', () => { const now = DateTime.now(); - const update = new AssetReplacedDto(2, 2, 'image/jpeg', true, 2, 2, null); + const update = new AssetReplacedDto(2, 2, 'image/jpeg', true, 2, 2, new Version('1')); - const asset_1 = new AssetDto('1', 'other', 'other', DateTime.today(), DateTime.today(), 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, null); + const asset_1 = new AssetDto('1', 'other', 'other', DateTime.today(), DateTime.today(), 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, new Version('1')); const asset_2 = asset_1.update(update, 'me', now); expect(asset_2.fileSize).toEqual(2); @@ -48,6 +48,7 @@ describe('AssetDto', () => { expect(asset_2.pixelHeight).toEqual(2); expect(asset_2.lastModified).toEqual(now); expect(asset_2.lastModifiedBy).toEqual('me'); + expect(asset_2.version).toEqual(update); }); }); @@ -160,7 +161,7 @@ describe('AssetsService', () => { let asset: AssetDto | null = null; - assetsService.getAsset('my-app', '123').subscribe(result => { + assetsService.getAsset('my-app', '123', version).subscribe(result => { asset = result; }); @@ -211,14 +212,14 @@ describe('AssetsService', () => { let asset: AssetDto | null = null; - assetsService.getAsset('my-app', '123').subscribe(result => { + assetsService.getAsset('my-app', '123', version).subscribe(result => { asset = result; }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets/123'); expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('If-Match')).toBeNull(); + expect(req.request.headers.get('If-Match')).toEqual(version.value); req.flush({}, { status: 404, statusText: '404' }); @@ -246,7 +247,7 @@ describe('AssetsService', () => { const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets?mimeTypes=image/png,image/png&take=17&skip=13'); expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('If-Match')).toBeNull(); + expect(req.request.headers.get('If-Match')).toEqual(version.value); req.flush({ total: 10, items: [] }); })); @@ -259,7 +260,7 @@ describe('AssetsService', () => { const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets?ids=12,23&take=17&skip=13'); expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('If-Match')).toBeNull(); + expect(req.request.headers.get('If-Match')).toEqual(version.value); req.flush({ total: 10, items: [] }); })); @@ -276,7 +277,7 @@ describe('AssetsService', () => { const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets'); expect(req.request.method).toEqual('POST'); - expect(req.request.headers.get('If-Match')).toBeNull(); + expect(req.request.headers.get('If-Match')).toEqual(version.value); req.flush({ id: 'id1', diff --git a/src/Squidex/app/shared/services/assets.service.ts b/src/Squidex/app/shared/services/assets.service.ts index bcba00158..6e839956f 100644 --- a/src/Squidex/app/shared/services/assets.service.ts +++ b/src/Squidex/app/shared/services/assets.service.ts @@ -163,7 +163,7 @@ export class AssetsService { return this.http.request(req) .map(event => { if (event.type === HttpEventType.UploadProgress) { - const percentDone = Math.round(100 * event.loaded / event.total); + const percentDone = event.total ? Math.round(100 * event.loaded / event.total) : 0; return percentDone; } else if (event instanceof HttpResponse) { @@ -235,20 +235,21 @@ export class AssetsService { .pretifyError('Failed to load assets. Please reload.'); } - public replaceFile(appName: string, id: string, file: File, version?: Version): Observable { + public replaceFile(appName: string, id: string, file: File, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}/content`); - const req = new HttpRequest('PUT', url, getFormData(file), { - headers: new HttpHeaders({ - 'If-Match': version.value - }), - reportProgress: true - }); + const headers = new HttpHeaders(); + + if (version) { + headers.set('If-Match', version.value); + } + + const req = new HttpRequest('PUT', url, getFormData(file), { headers, reportProgress: true }); return this.http.request(req) .map(event => { if (event.type === HttpEventType.UploadProgress) { - const percentDone = Math.round(100 * event.loaded / event.total); + const percentDone = event.total ? Math.round(100 * event.loaded / event.total) : 0; return percentDone; } else if (event instanceof HttpResponse) { @@ -269,7 +270,7 @@ export class AssetsService { .pretifyError('Failed to replace asset. Please reload.'); } - public deleteAsset(appName: string, id: string, version?: Version): Observable { + public deleteAsset(appName: string, id: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`); return HTTP.deleteVersioned(this.http, url, version) @@ -279,7 +280,7 @@ export class AssetsService { .pretifyError('Failed to delete asset. Please reload.'); } - public putAsset(appName: string, id: string, dto: UpdateAssetDto, version?: Version): Observable { + public putAsset(appName: string, id: string, dto: UpdateAssetDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets/${id}`); return HTTP.putVersioned(this.http, url, dto, version) diff --git a/src/Squidex/app/shared/services/auth.service.ts b/src/Squidex/app/shared/services/auth.service.ts index 8970884ce..efdfb970a 100644 --- a/src/Squidex/app/shared/services/auth.service.ts +++ b/src/Squidex/app/shared/services/auth.service.ts @@ -39,7 +39,7 @@ export class Profile { } public get authToken(): string { - return `${this.user.token_type} ${this.user.access_token}`; + return `${this.user!.token_type} ${this.user.access_token}`; } public get token(): string { @@ -56,7 +56,7 @@ export class Profile { export class AuthService { private readonly userManager: UserManager; private readonly user$ = new ReplaySubject(1); - private currentUser: Profile = null; + private currentUser: Profile | null = null; public get user(): Profile | null { return this.currentUser; @@ -150,7 +150,7 @@ export class AuthService { public loginSilent(): Observable { const observable: Observable = - Observable.create((observer: Observer) => { + Observable.create((observer: Observer) => { this.userManager.signinSilent() .then(x => { observer.next(this.createProfile(x)); @@ -169,13 +169,17 @@ export class AuthService { .concat(Observable.throw(new Error('Retry limit exceeded.')))); } - private createProfile(user: User) { - return user ? new Profile(user) : null; + private createProfile(user: User): Profile { + return new Profile(user); } - private checkState(promise: Promise) { + private checkState(promise: Promise) { promise.then(user => { - this.user$.next(this.createProfile(user)); + if (user) { + this.user$.next(this.createProfile(user)); + } else { + this.user$.next(null); + } return true; }, err => { diff --git a/src/Squidex/app/shared/services/contents.service.spec.ts b/src/Squidex/app/shared/services/contents.service.spec.ts index 849c9f77e..54a0a92d1 100644 --- a/src/Squidex/app/shared/services/contents.service.spec.ts +++ b/src/Squidex/app/shared/services/contents.service.spec.ts @@ -22,7 +22,7 @@ describe('ContentDto', () => { it('should update data property and user info when updating', () => { const now = DateTime.now(); - const content_1 = new ContentDto('1', false, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, null); + const content_1 = new ContentDto('1', false, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, new Version('1')); const content_2 = content_1.update({ data: 2 }, 'me', now); expect(content_2.data).toEqual({ data: 2 }); @@ -33,7 +33,7 @@ describe('ContentDto', () => { it('should update isPublished property and user info when publishing', () => { const now = DateTime.now(); - const content_1 = new ContentDto('1', false, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, null); + const content_1 = new ContentDto('1', false, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, new Version('1')); const content_2 = content_1.publish('me', now); expect(content_2.isPublished).toBeTruthy(); @@ -44,7 +44,7 @@ describe('ContentDto', () => { it('should update isPublished property and user info when unpublishing', () => { const now = DateTime.now(); - const content_1 = new ContentDto('1', true, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, null); + const content_1 = new ContentDto('1', true, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, new Version('1')); const content_2 = content_1.unpublish('me', now); expect(content_2.isPublished).toBeFalsy(); @@ -55,7 +55,7 @@ describe('ContentDto', () => { it('should update data property when setting data', () => { const newData = {}; - const content_1 = new ContentDto('1', true, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, null); + const content_1 = new ContentDto('1', true, false, 'other', 'other', DateTime.now(), DateTime.now(), { data: 1 }, new Version('1')); const content_2 = content_1.setData(newData); expect(content_2.data).toBe(newData); @@ -140,7 +140,7 @@ describe('ContentsService', () => { it('should append query to get request as search', inject([ContentsService, HttpTestingController], (contentsService: ContentsService, httpMock: HttpTestingController) => { - let contents: ContentsDto | null; + let contents: ContentsDto | null = null; contentsService.getContents('my-app', 'my-schema', 17, 13, 'my-query').subscribe(result => { contents = result; @@ -157,9 +157,9 @@ describe('ContentsService', () => { it('should append ids to get request with ids', inject([ContentsService, HttpTestingController], (contentsService: ContentsService, httpMock: HttpTestingController) => { - let contents: ContentsDto | null; + let contents: ContentsDto | null = null; - contentsService.getContents('my-app', 'my-schema', 17, 13, null, ['id1', 'id2']).subscribe(result => { + contentsService.getContents('my-app', 'my-schema', 17, 13, undefined, ['id1', 'id2']).subscribe(result => { contents = result; }); @@ -174,7 +174,7 @@ describe('ContentsService', () => { it('should append query to get request as plain query string', inject([ContentsService, HttpTestingController], (contentsService: ContentsService, httpMock: HttpTestingController) => { - let contents: ContentsDto | null; + let contents: ContentsDto | null = null; contentsService.getContents('my-app', 'my-schema', 17, 13, '$filter=my-filter').subscribe(result => { contents = result; diff --git a/src/Squidex/app/shared/services/contents.service.ts b/src/Squidex/app/shared/services/contents.service.ts index 0e70d45a9..ea2e47dab 100644 --- a/src/Squidex/app/shared/services/contents.service.ts +++ b/src/Squidex/app/shared/services/contents.service.ts @@ -176,7 +176,7 @@ export class ContentsService { .pretifyError('Failed to load content. Please reload.'); } - public postContent(appName: string, schemaName: string, dto: any, publish: boolean, version?: Version): Observable { + public postContent(appName: string, schemaName: string, dto: any, publish: boolean, version: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?publish=${publish}`); return HTTP.postVersioned(this.http, url, dto, version) @@ -197,7 +197,7 @@ export class ContentsService { .pretifyError('Failed to create content. Please reload.'); } - public putContent(appName: string, schemaName: string, id: string, dto: any, version?: Version): Observable { + public putContent(appName: string, schemaName: string, id: string, dto: any, version: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`); return HTTP.putVersioned(this.http, url, dto, version) @@ -207,8 +207,8 @@ export class ContentsService { .pretifyError('Failed to update content. Please reload.'); } - public deleteContent(appName: string, schemaName: string, id: string, version?: Version): Observable { - const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}`); + public deleteContent(appName: string, schemaName: string, id: string, version: Version): Observable { + const url = this.apiUrl.buildUrl(`/api/coentent/${appName}/${schemaName}/${id}`); return HTTP.deleteVersioned(this.http, url, version) .do(() => { @@ -217,21 +217,21 @@ export class ContentsService { .pretifyError('Failed to delete content. Please reload.'); } - public getVersionData(appName: string, schemaName: string, id: string, version?: Version): Observable { + public getVersionData(appName: string, schemaName: string, id: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/${version.value}`); return HTTP.getVersioned(this.http, url, version) .pretifyError('Failed to load data. Please reload.'); } - public publishContent(appName: string, schemaName: string, id: string, version?: Version): Observable { + public publishContent(appName: string, schemaName: string, id: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/publish`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to publish content. Please reload.'); } - public unpublishContent(appName: string, schemaName: string, id: string, version?: Version): Observable { + public unpublishContent(appName: string, schemaName: string, id: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}/${id}/unpublish`); return HTTP.putVersioned(this.http, url, {}, version) diff --git a/src/Squidex/app/shared/services/plans.service.ts b/src/Squidex/app/shared/services/plans.service.ts index 0a76a0ac0..afb40fded 100644 --- a/src/Squidex/app/shared/services/plans.service.ts +++ b/src/Squidex/app/shared/services/plans.service.ts @@ -85,7 +85,7 @@ export class PlansService { .pretifyError('Failed to load plans. Please reload.'); } - public putPlan(appName: string, dto: ChangePlanDto, version?: Version): Observable { + public putPlan(appName: string, dto: ChangePlanDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/plan`); return HTTP.putVersioned(this.http, url, dto, version) diff --git a/src/Squidex/app/shared/services/schemas.service.spec.ts b/src/Squidex/app/shared/services/schemas.service.spec.ts index d2ec60e7d..7018ff6d6 100644 --- a/src/Squidex/app/shared/services/schemas.service.spec.ts +++ b/src/Squidex/app/shared/services/schemas.service.spec.ts @@ -27,12 +27,12 @@ import { } from './../'; describe('SchemaDto', () => { - const properties = new SchemaPropertiesDto('Name', null); + const properties = new SchemaPropertiesDto('Name'); it('should update isPublished property and user info when publishing', () => { const now = DateTime.now(); - const schema_1 = new SchemaDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null); + const schema_1 = new SchemaDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1')); const schema_2 = schema_1.publish('me', now); expect(schema_2.isPublished).toBeTruthy(); @@ -43,7 +43,7 @@ describe('SchemaDto', () => { it('should update isPublished property and user info when unpublishing', () => { const now = DateTime.now(); - const schema_1 = new SchemaDto('1', 'name', properties, true, 'other', 'other', DateTime.now(), DateTime.now(), null); + const schema_1 = new SchemaDto('1', 'name', properties, true, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1')); const schema_2 = schema_1.unpublish('me', now); expect(schema_2.isPublished).toBeFalsy(); @@ -52,11 +52,11 @@ describe('SchemaDto', () => { }); it('should update properties property and user info when updating', () => { - const newProperties = new SchemaPropertiesDto('New Name', null); + const newProperties = new SchemaPropertiesDto('New Name'); const now = DateTime.now(); - const schema_1 = new SchemaDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null); + const schema_1 = new SchemaDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1')); const schema_2 = schema_1.update(newProperties, 'me', now); expect(schema_2.properties).toEqual(newProperties); @@ -76,7 +76,7 @@ describe('SchemaDto', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, []); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), []); const schema_2 = schema_1.configureScripts(newScripts, 'me', now); expect(schema_2.scriptQuery).toEqual(''); @@ -91,12 +91,12 @@ describe('SchemaDto', () => { }); describe('SchemaDetailsDto', () => { - const properties = new SchemaPropertiesDto('Name', null); + const properties = new SchemaPropertiesDto('Name'); it('should update isPublished property and user info when publishing', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, []); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), []); const schema_2 = schema_1.publish('me', now); expect(schema_2.isPublished).toBeTruthy(); @@ -107,7 +107,7 @@ describe('SchemaDetailsDto', () => { it('should update isPublished property and user info when unpublishing', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, true, 'other', 'other', DateTime.now(), DateTime.now(), null, []); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, true, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), []); const schema_2 = schema_1.unpublish('me', now); expect(schema_2.isPublished).toBeFalsy(); @@ -116,11 +116,11 @@ describe('SchemaDetailsDto', () => { }); it('should update properties property and user info when updating', () => { - const newProperties = new SchemaPropertiesDto('New Name', null); + const newProperties = new SchemaPropertiesDto('New Name'); const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, []); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), []); const schema_2 = schema_1.update(newProperties, 'me', now); expect(schema_2.properties).toEqual(newProperties); @@ -134,7 +134,7 @@ describe('SchemaDetailsDto', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, [field1]); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), [field1]); const schema_2 = schema_1.addField(field2, 'me', now); expect(schema_2.fields).toEqual([field1, field2]); @@ -148,7 +148,7 @@ describe('SchemaDetailsDto', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, [field1, field2]); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), [field1, field2]); const schema_2 = schema_1.removeField(field1, 'me', now); expect(schema_2.fields).toEqual([field2]); @@ -162,7 +162,7 @@ describe('SchemaDetailsDto', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, [field1, field2]); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), [field1, field2]); const schema_2 = schema_1.replaceFields([field2, field1], 'me', now); expect(schema_2.fields).toEqual([field2, field1]); @@ -177,7 +177,7 @@ describe('SchemaDetailsDto', () => { const now = DateTime.now(); - const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), null, [field1_0, field2_1]); + const schema_1 = new SchemaDetailsDto('1', 'name', properties, false, 'other', 'other', DateTime.now(), DateTime.now(), new Version('1'), [field1_0, field2_1]); const schema_2 = schema_1.updateField(field2_2, 'me', now); expect(schema_2.fields).toEqual([field1_0, field2_2]); @@ -461,7 +461,7 @@ describe('SchemasService', () => { req.flush({ id: '1' }); expect(schema).toEqual( - new SchemaDetailsDto('1', dto.name, new SchemaPropertiesDto(null, null), false, user, user, now, now, version, [])); + new SchemaDetailsDto('1', dto.name, new SchemaPropertiesDto(), false, user, user, now, now, version, [])); })); it('should make post request to add field', diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index 4d15958a1..42ca23c75 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -643,8 +643,8 @@ export class JsonFieldPropertiesDto extends FieldPropertiesDto { export class SchemaPropertiesDto { constructor( - public readonly label: string, - public readonly hints: string + public readonly label?: string, + public readonly hints?: string ) { } } @@ -781,7 +781,7 @@ export class SchemasService { .pretifyError('Failed to load schema. Please reload.'); } - public postSchema(appName: string, dto: CreateSchemaDto, user: string, now?: DateTime, version?: Version): Observable { + public postSchema(appName: string, dto: CreateSchemaDto, user: string, now: DateTime, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`); return HTTP.postVersioned(this.http, url, dto, version) @@ -791,13 +791,13 @@ export class SchemasService { return new SchemaDetailsDto( response.id, dto.name, - dto.properties || new SchemaPropertiesDto(null, null), + dto.properties || new SchemaPropertiesDto(), false, user, user, now, now, - version, + version || new Version('0'), dto.fields || [], response.scriptQuery, response.scriptCreate, @@ -813,7 +813,7 @@ export class SchemasService { .pretifyError('Failed to create schema. Please reload.'); } - public postField(appName: string, schemaName: string, dto: AddFieldDto, version?: Version): Observable { + public postField(appName: string, schemaName: string, dto: AddFieldDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields`); return HTTP.postVersioned(this.http, url, dto, version) @@ -830,7 +830,7 @@ export class SchemasService { .pretifyError('Failed to add field. Please reload.'); } - public deleteSchema(appName: string, schemaName: string, version?: Version): Observable { + public deleteSchema(appName: string, schemaName: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); return HTTP.deleteVersioned(this.http, url, version) @@ -840,84 +840,84 @@ export class SchemasService { .pretifyError('Failed to delete schema. Please reload.'); } - public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version?: Version): Observable { + public putSchemaScripts(appName: string, schemaName: string, dto: UpdateSchemaScriptsDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/scripts`); return HTTP.putVersioned(this.http, url, dto, version) .pretifyError('Failed to update schema scripts. Please reload.'); } - public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version?: Version): Observable { + public putSchema(appName: string, schemaName: string, dto: UpdateSchemaDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}`); return HTTP.putVersioned(this.http, url, dto, version) .pretifyError('Failed to update schema. Please reload.'); } - public putFieldOrdering(appName: string, schemaName: string, dto: number[], version?: Version): Observable { + public putFieldOrdering(appName: string, schemaName: string, dto: number[], version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/ordering`); return HTTP.putVersioned(this.http, url, { fieldIds: dto }, version) .pretifyError('Failed to reorder fields. Please reload.'); } - public publishSchema(appName: string, schemaName: string, version?: Version): Observable { + public publishSchema(appName: string, schemaName: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to publish schema. Please reload.'); } - public unpublishSchema(appName: string, schemaName: string, version?: Version): Observable { + public unpublishSchema(appName: string, schemaName: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/unpublish`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to unpublish schema. Please reload.'); } - public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version?: Version): Observable { + public putField(appName: string, schemaName: string, fieldId: number, dto: UpdateFieldDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`); return HTTP.putVersioned(this.http, url, dto, version) .pretifyError('Failed to update field. Please reload.'); } - public enableField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { + public enableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/enable`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to enable field. Please reload.'); } - public disableField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { + public disableField(appName: string, schemaName: string, fieldId: number, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/disable`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to disable field. Please reload.'); } - public lockField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { + public lockField(appName: string, schemaName: string, fieldId: number, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/lock`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to lock field. Please reload.'); } - public showField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { + public showField(appName: string, schemaName: string, fieldId: number, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/show`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to show field. Please reload.'); } - public hideField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { + public hideField(appName: string, schemaName: string, fieldId: number, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/hide`); return HTTP.putVersioned(this.http, url, {}, version) .pretifyError('Failed to hide field. Please reload.'); } - public deleteField(appName: string, schemaName: string, fieldId: number, version?: Version): Observable { + public deleteField(appName: string, schemaName: string, fieldId: number, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}`); return HTTP.deleteVersioned(this.http, url, version) diff --git a/src/Squidex/app/shared/services/ui.service.spec.ts b/src/Squidex/app/shared/services/ui.service.spec.ts index a1cb05b2c..129dc3598 100644 --- a/src/Squidex/app/shared/services/ui.service.spec.ts +++ b/src/Squidex/app/shared/services/ui.service.spec.ts @@ -74,6 +74,7 @@ describe('UIService', () => { req.error(new ErrorEvent('500')); - expect(settings.regexSuggestions).toEqual([]); + expect(settings).toBeDefined(); + expect(settings!.regexSuggestions).toEqual([]); })); }); \ No newline at end of file diff --git a/src/Squidex/app/shared/services/webhooks.service.spec.ts b/src/Squidex/app/shared/services/webhooks.service.spec.ts index 22eedd991..52a22513f 100644 --- a/src/Squidex/app/shared/services/webhooks.service.spec.ts +++ b/src/Squidex/app/shared/services/webhooks.service.spec.ts @@ -67,14 +67,14 @@ describe('WebhooksService', () => { let webhooks: WebhookDto[] | null = null; - webhooksService.getWebhooks('my-app', version).subscribe(result => { + webhooksService.getWebhooks('my-app').subscribe(result => { webhooks = result; }); const req = httpMock.expectOne('http://service/p/api/apps/my-app/webhooks'); expect(req.request.method).toEqual('GET'); - expect(req.request.headers.get('If-Match')).toBe(version.value); + expect(req.request.headers.get('If-Match')).toBeNull(); req.flush([ { diff --git a/src/Squidex/app/shared/services/webhooks.service.ts b/src/Squidex/app/shared/services/webhooks.service.ts index ad31209bc..8e9b73903 100644 --- a/src/Squidex/app/shared/services/webhooks.service.ts +++ b/src/Squidex/app/shared/services/webhooks.service.ts @@ -111,10 +111,10 @@ export class WebhooksService { ) { } - public getWebhooks(appName: string, version?: Version): Observable { + public getWebhooks(appName: string): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks`); - return HTTP.getVersioned(this.http, url, version) + return HTTP.getVersioned(this.http, url) .map(response => { const items: any[] = response; @@ -147,7 +147,7 @@ export class WebhooksService { .pretifyError('Failed to load webhooks. Please reload.'); } - public postWebhook(appName: string, dto: CreateWebhookDto, user: string, now?: DateTime, version?: Version): Observable { + public postWebhook(appName: string, dto: CreateWebhookDto, user: string, now: DateTime, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks`); return HTTP.postVersioned(this.http, url, dto, version) @@ -167,14 +167,14 @@ export class WebhooksService { .pretifyError('Failed to create webhook. Please reload.'); } - public putWebhook(appName: string, id: string, dto: UpdateWebhookDto, version?: Version): Observable { + public putWebhook(appName: string, id: string, dto: UpdateWebhookDto, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks/${id}`); return HTTP.putVersioned(this.http, url, dto, version) .pretifyError('Failed to update webhook. Please reload.'); } - public deleteWebhook(appName: string, id: string, version?: Version): Observable { + public deleteWebhook(appName: string, id: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/webhooks/${id}`); return HTTP.deleteVersioned(this.http, url, version) diff --git a/src/Squidex/app/shell/pages/internal/profile-menu.component.ts b/src/Squidex/app/shell/pages/internal/profile-menu.component.ts index 1f9ebbdc5..e5cb98ef1 100644 --- a/src/Squidex/app/shell/pages/internal/profile-menu.component.ts +++ b/src/Squidex/app/shell/pages/internal/profile-menu.component.ts @@ -49,10 +49,10 @@ export class ProfileMenuComponent implements OnDestroy, OnInit { this.authenticationSubscription = this.authService.userChanges.filter(user => !!user) .subscribe(user => { - this.profileId = user.id; - this.profileDisplayName = user.displayName; + this.profileId = user!.id; + this.profileDisplayName = user!.displayName; - this.isAdmin = user.isAdmin; + this.isAdmin = user!.isAdmin; }); } diff --git a/src/Squidex/tsconfig.json b/src/Squidex/tsconfig.json index 54692ff9c..b740cdfc7 100644 --- a/src/Squidex/tsconfig.json +++ b/src/Squidex/tsconfig.json @@ -9,7 +9,7 @@ "noUnusedParameters": false, "removeComments": false, "sourceMap": true, - "strictNullChecks": false, + "strictNullChecks": true, "suppressImplicitAnyIndexErrors": true, "target": "es5", "paths": {