Browse Source

Bugfixes for contributors and form comparison.

pull/411/head
Sebastian Stehle 7 years ago
parent
commit
3e31a9a2d8
  1. 18
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  2. 6
      src/Squidex/app/features/content/shared/array-editor.component.html
  3. 8
      src/Squidex/app/features/content/shared/array-editor.component.ts
  4. 2
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html
  5. 2
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts
  6. 20
      src/Squidex/app/framework/angular/forms/forms-helper.ts
  7. 2
      src/Squidex/app/framework/angular/modals/modal-dialog.component.ts
  8. 22
      src/Squidex/app/framework/state.ts
  9. 12
      src/Squidex/app/framework/utils/error.ts
  10. 28
      src/Squidex/app/shared/state/contents.forms.ts
  11. 10
      src/Squidex/app/shared/state/contributors.forms.ts
  12. 4
      src/Squidex/app/shared/state/contributors.state.ts

18
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -117,14 +117,14 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
if (content) { if (content) {
this.content = content; this.content = content;
this.loadContent(this.content.dataDraft); this.loadContent(this.content.dataDraft, true);
} }
if (autosaved) { if (autosaved) {
this.dialogs.confirm('Unsaved changes', 'You have unsaved changes. Do you want to load them now?') this.dialogs.confirm('Unsaved changes', 'You have unsaved changes. Do you want to load them now?')
.subscribe(shouldLoad => { .subscribe(shouldLoad => {
if (shouldLoad) { if (shouldLoad) {
this.loadContent(autosaved); this.loadContent(autosaved, false);
} else { } else {
this.autoSaveService.remove(this.autoSaveKey); this.autoSaveService.remove(this.autoSaveKey);
} }
@ -149,7 +149,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
} }
public canDeactivate(): Observable<boolean> { public canDeactivate(): Observable<boolean> {
if (!this.contentForm.hasChanged(this.content)) { if (!this.contentForm.hasChanged()) {
return of(true); return of(true);
} else { } 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( 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 }); 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.isLoadingContent = true;
this.autoSaveService.remove(this.autoSaveKey); this.autoSaveService.remove(this.autoSaveKey);
try { try {
this.contentForm.loadContent(data); this.contentForm.load(data, isInitial);
this.contentForm.setEnabled(!this.content || this.content.canUpdateAny); this.contentForm.setEnabled(!this.content || this.content.canUpdateAny);
} finally { } finally {
this.isLoadingContent = false; this.isLoadingContent = false;
@ -267,7 +267,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
if (!this.content || version === null || version.eq(this.content.version)) { if (!this.content || version === null || version.eq(this.content.version)) {
this.contentFormCompare = null; this.contentFormCompare = null;
this.contentVersion = null; this.contentVersion = null;
this.loadContent(this.content.dataDraft); this.loadContent(this.content.dataDraft, true);
} else { } else {
this.contentsState.loadVersion(this.content, version) this.contentsState.loadVersion(this.content, version)
.subscribe(dto => { .subscribe(dto => {
@ -276,16 +276,16 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
this.contentFormCompare = new EditContentForm(this.schema, this.languages); this.contentFormCompare = new EditContentForm(this.schema, this.languages);
} }
this.contentFormCompare.loadContent(dto.payload); this.contentFormCompare.load(dto.payload);
this.contentFormCompare.setEnabled(false); this.contentFormCompare.setEnabled(false);
this.loadContent(this.content.dataDraft); this.loadContent(this.content.dataDraft, false);
} else { } else {
if (this.contentFormCompare) { if (this.contentFormCompare) {
this.contentFormCompare = null; this.contentFormCompare = null;
} }
this.loadContent(dto.payload); this.loadContent(dto.payload, false);
} }
this.contentVersion = version; this.contentVersion = version;

6
src/Squidex/app/features/content/shared/array-editor.component.html

@ -14,15 +14,15 @@
[itemForm]="itemForm" [itemForm]="itemForm"
[language]="language" [language]="language"
[languages]="languages" [languages]="languages"
(clone)="addItem(itemForm)" (clone)="itemAdd(itemForm)"
(move)="move(itemForm, $event)" (move)="move(itemForm, $event)"
(remove)="removeItem(i)" (remove)="itemRemove(i)"
(toggle)="hide($event)"> (toggle)="hide($event)">
</sqx-array-item> </sqx-array-item>
</div> </div>
</div> </div>
<button type="button" class="btn btn-success" [disabled]="field.nested.length === 0 || arrayControl.disabled" (click)="addItem(undefined)"> <button type="button" class="btn btn-success" [disabled]="field.nested.length === 0 || arrayControl.disabled" (click)="itemAdd(undefined)">
Add Item Add Item
</button> </button>

8
src/Squidex/app/features/content/shared/array-editor.component.ts

@ -54,12 +54,12 @@ export class ArrayEditorComponent extends StatefulComponent<State> {
this.next(s => ({ ...s, isHidden })); this.next(s => ({ ...s, isHidden }));
} }
public removeItem(index: number) { public itemRemove(index: number) {
this.form.removeArrayItem(this.field, this.language, index); this.form.arrayItemRemove(this.field, this.language, index);
} }
public addItem(value?: FormGroup) { public itemAdd(value?: FormGroup) {
this.form.insertArrayItem(this.field, this.language, value); this.form.arrayItemInsert(this.field, this.language, value);
} }
public sort(controls: AbstractControl[]) { public sort(controls: AbstractControl[]) {

2
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html

@ -92,7 +92,7 @@
</div> </div>
<sqx-form-hint class="text-right"> <sqx-form-hint class="text-right">
Big team? <a class="force" (click)="importDialog.show()">Hide many contributors at once</a> Big team? <a class="force" (click)="importDialog.show()">Add many contributors at once</a>
</sqx-form-hint> </sqx-form-hint>
</ng-container> </ng-container>
</ng-container> </ng-container>

2
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts

@ -108,6 +108,8 @@ export class ContributorsPageComponent implements OnInit {
if (isCreated) { if (isCreated) {
this.dialogs.notifyInfo('A new user with the entered email address has been created and assigned as contributor.'); 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 => { }, error => {
this.assignContributorForm.submitFailed(error); this.assignContributorForm.submitFailed(error);

20
src/Squidex/app/framework/angular/forms/forms-helper.ts

@ -37,25 +37,11 @@ export function hasNoValue$(form: AbstractControl): Observable<boolean> {
return value$(form).pipe(map(v => !v)); return value$(form).pipe(map(v => !v));
} }
export function fullValue(form: AbstractControl): any { export function getRawValue(form: AbstractControl): any {
if (Types.is(form, FormGroup)) { if (Types.is(form, FormGroup)) {
const groupValue = {}; return form.getRawValue();
for (let key in form.controls) {
if (form.controls.hasOwnProperty(key)) {
groupValue[key] = fullValue(form.controls[key]);
}
}
return groupValue;
} else if (Types.is(form, FormArray)) { } else if (Types.is(form, FormArray)) {
const arrayValue = []; return form.getRawValue();
for (let child of form.controls) {
arrayValue.push(fullValue(child));
}
return arrayValue;
} else { } else {
return form.value; return form.value;
} }

2
src/Squidex/app/framework/angular/modals/modal-dialog.component.ts

@ -68,7 +68,7 @@ export class ModalDialogComponent extends StatefulComponent<State> implements Af
const hasTabs = this.tabsElement.nativeElement.children.length > 0; const hasTabs = this.tabsElement.nativeElement.children.length > 0;
const hasFooter = this.footerElement.nativeElement.children.length > 0; const hasFooter = this.footerElement.nativeElement.children.length > 0;
this.next(() => ({ hasTabs, hasFooter })); this.next({ hasTabs, hasFooter });
} }
public emitClose() { public emitClose() {

22
src/Squidex/app/framework/state.ts

@ -9,13 +9,13 @@ import { AbstractControl } from '@angular/forms';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { ErrorDto } from './utils/error'; import { ErrorDto, getDisplayMessage } from './utils/error';
import { ResourceLinks } from './utils/hateos'; import { ResourceLinks } from './utils/hateos';
import { Types } from './utils/types'; import { Types } from './utils/types';
import { fullValue } from './angular/forms/forms-helper'; import { getRawValue } from './angular/forms/forms-helper';
export interface FormState { export interface FormState {
submitted: boolean; submitted: boolean;
@ -70,16 +70,16 @@ export class Form<T extends AbstractControl, V> {
} }
public load(value: V | undefined) { public load(value: V | undefined) {
this.state.next(() => ({ submitted: false, error: null })); this.state.next({ submitted: false, error: null });
this.setValue(value); this.setValue(value);
} }
public submit(): V | null { public submit(): V | null {
this.state.next(() => ({ submitted: true })); this.state.next({ submitted: true, error: null });
if (this.form.valid) { if (this.form.valid) {
const value = this.transformSubmit(fullValue(this.form)); const value = this.transformSubmit(getRawValue(this.form));
if (value) { if (value) {
this.disable(); this.disable();
@ -92,7 +92,7 @@ export class Form<T extends AbstractControl, V> {
} }
public submitCompleted(options?: { newValue?: V, noReset?: boolean }) { public submitCompleted(options?: { newValue?: V, noReset?: boolean }) {
this.state.next(() => ({ submitted: false, error: null })); this.state.next({ submitted: false, error: null });
this.enable(); this.enable();
@ -104,18 +104,10 @@ export class Form<T extends AbstractControl, V> {
} }
public submitFailed(error?: string | ErrorDto) { public submitFailed(error?: string | ErrorDto) {
this.state.next(() => ({ submitted: false, error: this.getError(error) })); this.state.next({ submitted: false, error: getDisplayMessage(error) });
this.enable(); this.enable();
} }
private getError(error?: string | ErrorDto) {
if (Types.is(error, ErrorDto)) {
return error.displayMessage;
} else {
return error;
}
}
} }
export class Model<T> { export class Model<T> {

12
src/Squidex/app/framework/utils/error.ts

@ -5,6 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Types } from './types';
export class ErrorDto { export class ErrorDto {
public readonly displayMessage: string; 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[]) { function formatMessage(message: string, details?: string[]) {
const appendLast = (row: string, char: string) => { const appendLast = (row: string, char: string) => {

28
src/Squidex/app/shared/state/contents.forms.ts

@ -395,7 +395,7 @@ export class FieldDefaultValue implements FieldPropertiesVisitor<any> {
} }
export class EditContentForm extends Form<FormGroup, any> { export class EditContentForm extends Form<FormGroup, any> {
private isLoaded = false; private initialData: any;
public value = value$(this.form); public value = value$(this.form);
@ -432,26 +432,22 @@ export class EditContentForm extends Form<FormGroup, any> {
} }
} }
this.initialData = this.form.getRawValue();
this.enable(); this.enable();
} }
public hasChanged(content?: ContentDto) { public hasChanged() {
if (!this.isLoaded && !this.form.touched) { const currentValue = this.form.getRawValue();
return false;
}
if (content) { return !Types.jsJsonEquals(this.initialData, currentValue);
return !Types.jsJsonEquals(this.form.value, content.data);
} else {
return true;
}
} }
public removeArrayItem(field: RootFieldDto, language: AppLanguageDto, index: number) { public arrayItemRemove(field: RootFieldDto, language: AppLanguageDto, index: number) {
this.findArrayItemForm(field, language).removeAt(index); 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) { if (field.nested.length > 0) {
const formControl = this.findArrayItemForm(field, language); const formControl = this.findArrayItemForm(field, language);
@ -495,9 +491,7 @@ export class EditContentForm extends Form<FormGroup, any> {
} }
} }
public loadContent(value: any) { public load(value: any, isInitial?: boolean) {
this.isLoaded = true;
for (let field of this.schema.fields) { for (let field of this.schema.fields) {
if (field.isArray && field.nested.length > 0) { if (field.isArray && field.nested.length > 0) {
const fieldForm = <FormGroup>this.form.get(field.name); const fieldForm = <FormGroup>this.form.get(field.name);
@ -534,6 +528,10 @@ export class EditContentForm extends Form<FormGroup, any> {
} }
super.load(value); super.load(value);
if (isInitial) {
this.initialData = this.form.getRawValue();
}
} }
public disable() { public disable() {

10
src/Squidex/app/shared/state/contributors.forms.ts

@ -32,12 +32,14 @@ export class AssignContributorForm extends Form<FormGroup, AssignContributorDto>
})); }));
} }
protected transformSubmit(value: string | UserDto) { protected transformSubmit(value: { user: string | UserDto }) {
if (Types.is(value, UserDto)) { let contributorId = value.user;
value = value.id;
if (Types.is(contributorId, UserDto)) {
contributorId = contributorId.id;
} }
return { contributorId: value, role: 'Editor', invite: true }; return { contributorId, role: 'Editor', invite: true };
} }
} }

4
src/Squidex/app/shared/state/contributors.state.ts

@ -7,7 +7,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { combineLatest, Observable, throwError } from 'rxjs'; import { combineLatest, Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators'; import { catchError, share, tap } from 'rxjs/operators';
import { import {
DialogService, DialogService,
@ -82,7 +82,7 @@ export class ContributorsState extends State<Snapshot> {
this.project(x => !!x.canCreate); this.project(x => !!x.canCreate);
public filtered = 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 = public contributorsPaged =
combineLatest(this.page, this.filtered, (p, c) => getPagedContributors(c, p)); combineLatest(this.page, this.filtered, (p, c) => getPagedContributors(c, p));

Loading…
Cancel
Save