From 3e31a9a2d80a593960db49a38970264a176ee636 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 5 Sep 2019 16:28:03 +0200 Subject: [PATCH] Bugfixes for contributors and form comparison. --- .../pages/content/content-page.component.ts | 18 ++++++------ .../shared/array-editor.component.html | 6 ++-- .../content/shared/array-editor.component.ts | 8 +++--- .../contributors-page.component.html | 2 +- .../contributors-page.component.ts | 2 ++ .../framework/angular/forms/forms-helper.ts | 20 ++----------- .../angular/modals/modal-dialog.component.ts | 2 +- src/Squidex/app/framework/state.ts | 22 +++++---------- src/Squidex/app/framework/utils/error.ts | 12 ++++++++ .../app/shared/state/contents.forms.ts | 28 +++++++++---------- .../app/shared/state/contributors.forms.ts | 10 ++++--- .../app/shared/state/contributors.state.ts | 4 +-- 12 files changed, 63 insertions(+), 71 deletions(-) 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 f48290c9f..bd282a741 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 @@ -117,14 +117,14 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD if (content) { this.content = content; - this.loadContent(this.content.dataDraft); + this.loadContent(this.content.dataDraft, true); } if (autosaved) { this.dialogs.confirm('Unsaved changes', 'You have unsaved changes. Do you want to load them now?') .subscribe(shouldLoad => { if (shouldLoad) { - this.loadContent(autosaved); + this.loadContent(autosaved, false); } else { this.autoSaveService.remove(this.autoSaveKey); } @@ -149,7 +149,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD } public canDeactivate(): Observable { - if (!this.contentForm.hasChanged(this.content)) { + if (!this.contentForm.hasChanged()) { return of(true); } else { return this.dialogs.confirm('Unsaved changes', 'You have unsaved changes, do you want to close the current content view and discard your changes?').pipe( @@ -227,13 +227,13 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD this.router.navigate([this.schema.name], { relativeTo: this.route.parent!.parent, replaceUrl: true }); } - private loadContent(data: any) { + private loadContent(data: any, isInitial: boolean) { this.isLoadingContent = true; this.autoSaveService.remove(this.autoSaveKey); try { - this.contentForm.loadContent(data); + this.contentForm.load(data, isInitial); this.contentForm.setEnabled(!this.content || this.content.canUpdateAny); } finally { this.isLoadingContent = false; @@ -267,7 +267,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD if (!this.content || version === null || version.eq(this.content.version)) { this.contentFormCompare = null; this.contentVersion = null; - this.loadContent(this.content.dataDraft); + this.loadContent(this.content.dataDraft, true); } else { this.contentsState.loadVersion(this.content, version) .subscribe(dto => { @@ -276,16 +276,16 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD this.contentFormCompare = new EditContentForm(this.schema, this.languages); } - this.contentFormCompare.loadContent(dto.payload); + this.contentFormCompare.load(dto.payload); this.contentFormCompare.setEnabled(false); - this.loadContent(this.content.dataDraft); + this.loadContent(this.content.dataDraft, false); } else { if (this.contentFormCompare) { this.contentFormCompare = null; } - this.loadContent(dto.payload); + this.loadContent(dto.payload, false); } this.contentVersion = version; diff --git a/src/Squidex/app/features/content/shared/array-editor.component.html b/src/Squidex/app/features/content/shared/array-editor.component.html index 3e20eb4f2..b06c74385 100644 --- a/src/Squidex/app/features/content/shared/array-editor.component.html +++ b/src/Squidex/app/features/content/shared/array-editor.component.html @@ -14,15 +14,15 @@ [itemForm]="itemForm" [language]="language" [languages]="languages" - (clone)="addItem(itemForm)" + (clone)="itemAdd(itemForm)" (move)="move(itemForm, $event)" - (remove)="removeItem(i)" + (remove)="itemRemove(i)" (toggle)="hide($event)"> - diff --git a/src/Squidex/app/features/content/shared/array-editor.component.ts b/src/Squidex/app/features/content/shared/array-editor.component.ts index 92d199fab..b129646d9 100644 --- a/src/Squidex/app/features/content/shared/array-editor.component.ts +++ b/src/Squidex/app/features/content/shared/array-editor.component.ts @@ -54,12 +54,12 @@ export class ArrayEditorComponent extends StatefulComponent { this.next(s => ({ ...s, isHidden })); } - public removeItem(index: number) { - this.form.removeArrayItem(this.field, this.language, index); + public itemRemove(index: number) { + this.form.arrayItemRemove(this.field, this.language, index); } - public addItem(value?: FormGroup) { - this.form.insertArrayItem(this.field, this.language, value); + public itemAdd(value?: FormGroup) { + this.form.arrayItemInsert(this.field, this.language, value); } public sort(controls: AbstractControl[]) { diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html index e3bf65586..e61b3ff27 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html @@ -92,7 +92,7 @@ - Big team? Hide many contributors at once + Big team? Add many contributors at once diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts index ed698c636..fdc6143c7 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts @@ -108,6 +108,8 @@ export class ContributorsPageComponent implements OnInit { if (isCreated) { this.dialogs.notifyInfo('A new user with the entered email address has been created and assigned as contributor.'); + } else { + this.dialogs.notifyInfo('User has been added as contributor.'); } }, error => { this.assignContributorForm.submitFailed(error); diff --git a/src/Squidex/app/framework/angular/forms/forms-helper.ts b/src/Squidex/app/framework/angular/forms/forms-helper.ts index 8d2d507ec..6fd88d68a 100644 --- a/src/Squidex/app/framework/angular/forms/forms-helper.ts +++ b/src/Squidex/app/framework/angular/forms/forms-helper.ts @@ -37,25 +37,11 @@ export function hasNoValue$(form: AbstractControl): Observable { return value$(form).pipe(map(v => !v)); } -export function fullValue(form: AbstractControl): any { +export function getRawValue(form: AbstractControl): any { if (Types.is(form, FormGroup)) { - const groupValue = {}; - - for (let key in form.controls) { - if (form.controls.hasOwnProperty(key)) { - groupValue[key] = fullValue(form.controls[key]); - } - } - - return groupValue; + return form.getRawValue(); } else if (Types.is(form, FormArray)) { - const arrayValue = []; - - for (let child of form.controls) { - arrayValue.push(fullValue(child)); - } - - return arrayValue; + return form.getRawValue(); } else { return form.value; } diff --git a/src/Squidex/app/framework/angular/modals/modal-dialog.component.ts b/src/Squidex/app/framework/angular/modals/modal-dialog.component.ts index 5bc25ec37..819dc6c9c 100644 --- a/src/Squidex/app/framework/angular/modals/modal-dialog.component.ts +++ b/src/Squidex/app/framework/angular/modals/modal-dialog.component.ts @@ -68,7 +68,7 @@ export class ModalDialogComponent extends StatefulComponent implements Af const hasTabs = this.tabsElement.nativeElement.children.length > 0; const hasFooter = this.footerElement.nativeElement.children.length > 0; - this.next(() => ({ hasTabs, hasFooter })); + this.next({ hasTabs, hasFooter }); } public emitClose() { diff --git a/src/Squidex/app/framework/state.ts b/src/Squidex/app/framework/state.ts index 3d4fefbfc..5ea18719e 100644 --- a/src/Squidex/app/framework/state.ts +++ b/src/Squidex/app/framework/state.ts @@ -9,13 +9,13 @@ import { AbstractControl } from '@angular/forms'; import { BehaviorSubject, Observable } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; -import { ErrorDto } from './utils/error'; +import { ErrorDto, getDisplayMessage } from './utils/error'; import { ResourceLinks } from './utils/hateos'; import { Types } from './utils/types'; -import { fullValue } from './angular/forms/forms-helper'; +import { getRawValue } from './angular/forms/forms-helper'; export interface FormState { submitted: boolean; @@ -70,16 +70,16 @@ export class Form { } public load(value: V | undefined) { - this.state.next(() => ({ submitted: false, error: null })); + this.state.next({ submitted: false, error: null }); this.setValue(value); } public submit(): V | null { - this.state.next(() => ({ submitted: true })); + this.state.next({ submitted: true, error: null }); if (this.form.valid) { - const value = this.transformSubmit(fullValue(this.form)); + const value = this.transformSubmit(getRawValue(this.form)); if (value) { this.disable(); @@ -92,7 +92,7 @@ export class Form { } public submitCompleted(options?: { newValue?: V, noReset?: boolean }) { - this.state.next(() => ({ submitted: false, error: null })); + this.state.next({ submitted: false, error: null }); this.enable(); @@ -104,18 +104,10 @@ export class Form { } public submitFailed(error?: string | ErrorDto) { - this.state.next(() => ({ submitted: false, error: this.getError(error) })); + this.state.next({ submitted: false, error: getDisplayMessage(error) }); this.enable(); } - - private getError(error?: string | ErrorDto) { - if (Types.is(error, ErrorDto)) { - return error.displayMessage; - } else { - return error; - } - } } export class Model { diff --git a/src/Squidex/app/framework/utils/error.ts b/src/Squidex/app/framework/utils/error.ts index f395e1211..afaf9bc64 100644 --- a/src/Squidex/app/framework/utils/error.ts +++ b/src/Squidex/app/framework/utils/error.ts @@ -5,6 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ +import { Types } from './types'; + export class ErrorDto { public readonly displayMessage: string; @@ -22,6 +24,16 @@ export class ErrorDto { } } +export function getDisplayMessage(error?: string | ErrorDto) { + if (!error) { + return null; + } else if (Types.is(error, ErrorDto)) { + return error.displayMessage; + } else { + return error; + } +} + function formatMessage(message: string, details?: string[]) { const appendLast = (row: string, char: string) => { diff --git a/src/Squidex/app/shared/state/contents.forms.ts b/src/Squidex/app/shared/state/contents.forms.ts index 7b5df88ab..24c33aad4 100644 --- a/src/Squidex/app/shared/state/contents.forms.ts +++ b/src/Squidex/app/shared/state/contents.forms.ts @@ -395,7 +395,7 @@ export class FieldDefaultValue implements FieldPropertiesVisitor { } export class EditContentForm extends Form { - private isLoaded = false; + private initialData: any; public value = value$(this.form); @@ -432,26 +432,22 @@ export class EditContentForm extends Form { } } + this.initialData = this.form.getRawValue(); + this.enable(); } - public hasChanged(content?: ContentDto) { - if (!this.isLoaded && !this.form.touched) { - return false; - } + public hasChanged() { + const currentValue = this.form.getRawValue(); - if (content) { - return !Types.jsJsonEquals(this.form.value, content.data); - } else { - return true; - } + return !Types.jsJsonEquals(this.initialData, currentValue); } - public removeArrayItem(field: RootFieldDto, language: AppLanguageDto, index: number) { + public arrayItemRemove(field: RootFieldDto, language: AppLanguageDto, index: number) { this.findArrayItemForm(field, language).removeAt(index); } - public insertArrayItem(field: RootFieldDto, language: AppLanguageDto, source?: FormGroup) { + public arrayItemInsert(field: RootFieldDto, language: AppLanguageDto, source?: FormGroup) { if (field.nested.length > 0) { const formControl = this.findArrayItemForm(field, language); @@ -495,9 +491,7 @@ export class EditContentForm extends Form { } } - public loadContent(value: any) { - this.isLoaded = true; - + public load(value: any, isInitial?: boolean) { for (let field of this.schema.fields) { if (field.isArray && field.nested.length > 0) { const fieldForm = this.form.get(field.name); @@ -534,6 +528,10 @@ export class EditContentForm extends Form { } super.load(value); + + if (isInitial) { + this.initialData = this.form.getRawValue(); + } } public disable() { diff --git a/src/Squidex/app/shared/state/contributors.forms.ts b/src/Squidex/app/shared/state/contributors.forms.ts index 6cc83a631..887200a0e 100644 --- a/src/Squidex/app/shared/state/contributors.forms.ts +++ b/src/Squidex/app/shared/state/contributors.forms.ts @@ -32,12 +32,14 @@ export class AssignContributorForm extends Form })); } - protected transformSubmit(value: string | UserDto) { - if (Types.is(value, UserDto)) { - value = value.id; + protected transformSubmit(value: { user: string | UserDto }) { + let contributorId = value.user; + + if (Types.is(contributorId, UserDto)) { + contributorId = contributorId.id; } - return { contributorId: value, role: 'Editor', invite: true }; + return { contributorId, role: 'Editor', invite: true }; } } diff --git a/src/Squidex/app/shared/state/contributors.state.ts b/src/Squidex/app/shared/state/contributors.state.ts index 9afa670f3..fa30963a0 100644 --- a/src/Squidex/app/shared/state/contributors.state.ts +++ b/src/Squidex/app/shared/state/contributors.state.ts @@ -7,7 +7,7 @@ import { Injectable } from '@angular/core'; import { combineLatest, Observable, throwError } from 'rxjs'; -import { catchError, tap } from 'rxjs/operators'; +import { catchError, share, tap } from 'rxjs/operators'; import { DialogService, @@ -82,7 +82,7 @@ export class ContributorsState extends State { this.project(x => !!x.canCreate); public filtered = - combineLatest(this.queryRegex, this.contributors, (q, c) => getFilteredContributors(c, q)); + combineLatest(this.queryRegex, this.contributors, (q, c) => getFilteredContributors(c, q)).pipe(share()); public contributorsPaged = combineLatest(this.page, this.filtered, (p, c) => getPagedContributors(c, p));