Browse Source

Merge branch 'master' into feature-restore

pull/311/head
Sebastian 8 years ago
parent
commit
196ef2411f
  1. 4
      src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs
  2. 2
      src/Squidex.Domain.Apps.Events/Apps/AppPatternDeleted.cs
  3. 4
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html
  4. 9
      src/Squidex/app/framework/angular/forms/autocomplete.component.html
  5. 38
      src/Squidex/app/framework/angular/forms/autocomplete.component.ts
  6. 46
      src/Squidex/app/framework/angular/forms/tag-editor.component.html
  7. 7
      src/Squidex/app/framework/angular/forms/tag-editor.component.scss
  8. 132
      src/Squidex/app/framework/angular/forms/tag-editor.component.ts
  9. 6
      src/Squidex/app/framework/angular/http/http-extensions.ts
  10. 8
      src/Squidex/app/shared/components/asset.component.html
  11. 3
      src/Squidex/app/shared/components/asset.component.ts
  12. 3
      src/Squidex/app/shared/components/assets-list.component.html
  13. 4
      src/Squidex/app/shared/state/assets.state.ts
  14. 13
      src/Squidex/app/shared/state/contributors.state.ts
  15. 1
      src/Squidex/package.json

4
src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
"added pattern {[Name]}");
AddEventMessage<AppPatternDeleted>(
"deleted pattern {[Name]}");
"deleted pattern {[PatternId]}");
AddEventMessage<AppPatternUpdated>(
"updated pattern {[Name]}");
@ -164,7 +164,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Name", @event.Name));
.AddParameter("PatternId", @event.PatternId));
}
protected override Task<HistoryEventToStore> CreateEventCoreAsync(Envelope<IEvent> @event)

2
src/Squidex.Domain.Apps.Events/Apps/AppPatternDeleted.cs

@ -14,7 +14,5 @@ namespace Squidex.Domain.Apps.Events.Apps
public sealed class AppPatternDeleted : AppEvent
{
public Guid PatternId { get; set; }
public string Name { get; set; }
}
}

4
src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html

@ -60,11 +60,11 @@
</sqx-panel>
<sqx-modal-dialog *sqxModalView="eventConsumerErrorDialog;onRoot:true" (closed)="eventConsumerErrorDialog.hide()">
<ng-container #title>
<ng-container title>
Error
</ng-container>
<ng-container #content>
<ng-container content>
<textarea readonly class="form-control error-message">{{eventConsumerError}}</textarea>
</ng-container>
</sqx-modal-dialog>

9
src/Squidex/app/framework/angular/forms/autocomplete.component.html

@ -5,8 +5,13 @@
autocorrect="off"
autocapitalize="off">
<div *ngIf="items.length > 0" [sqxModalTarget]="input" class="control-dropdown" #container position="bottomLeft">
<div *ngFor="let item of items; let i = index;" class="control-dropdown-item control-dropdown-item-selectable" [class.active]="i === selectedIndex" (mousedown)="selectItem(item)" (mouseover)="selectIndex(i)" [sqxScrollActive]="i === selectedIndex" [container]="container">
<div *ngIf="suggestedItems.length > 0" [sqxModalTarget]="input" class="control-dropdown" #container position="bottomLeft">
<div *ngFor="let item of suggestedItems; let i = index;" class="control-dropdown-item control-dropdown-item-selectable"
[class.active]="i === suggestedIndex"
[container]="container"
(mousedown)="selectItem(item)"
(mouseover)="selectIndex(i)"
[sqxScrollActive]="i === suggestedIndex">
<ng-container *ngIf="!itemTemplate">{{item}}</ng-container>
<ng-template *ngIf="itemTemplate" [sqxTemplateWrapper]="itemTemplate" [item]="item" [index]="i"></ng-template>

38
src/Squidex/app/framework/angular/forms/autocomplete.component.ts

@ -49,9 +49,8 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
@ContentChild(TemplateRef)
public itemTemplate: TemplateRef<any>;
public items: any[] = [];
public selectedIndex = -1;
public suggestedItems: any[] = [];
public suggestedIndex = -1;
public queryInput = new FormControl();
@ -72,14 +71,13 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
this.reset();
}
}),
distinctUntilChanged(),
debounceTime(200),
distinctUntilChanged(),
filter(query => !!query && !!this.source),
switchMap(query => this.source.find(query)),
catchError(error => of([])))
switchMap(query => this.source.find(query)), catchError(() => of([])))
.subscribe(items => {
this.reset();
this.items = items || [];
this.suggestedIndex = -1;
this.suggestedItems = items || [];
});
}
@ -96,7 +94,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
this.reset();
return false;
case KEY_ENTER:
if (this.items.length > 0) {
if (this.suggestedItems.length > 0) {
this.selectItem();
return false;
}
@ -110,7 +108,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
if (!obj) {
this.resetForm();
} else {
const item = this.items.find(i => i === obj);
const item = this.suggestedItems.find(i => i === obj);
if (item) {
this.queryInput.setValue(obj.title || '');
@ -144,11 +142,11 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
public selectItem(selection: any | null = null) {
if (!selection) {
selection = this.items[this.selectedIndex];
selection = this.suggestedItems[this.suggestedIndex];
}
if (!selection && this.items.length === 1) {
selection = this.items[0];
if (!selection && this.suggestedItems.length === 1) {
selection = this.suggestedItems[0];
}
if (selection) {
@ -170,19 +168,19 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
selection = 0;
}
if (selection >= this.items.length) {
selection = this.items.length - 1;
if (selection >= this.suggestedItems.length) {
selection = this.suggestedItems.length - 1;
}
this.selectedIndex = selection;
this.suggestedIndex = selection;
}
private up() {
this.selectIndex(this.selectedIndex - 1);
this.selectIndex(this.suggestedIndex - 1);
}
private down() {
this.selectIndex(this.selectedIndex + 1);
this.selectIndex(this.suggestedIndex + 1);
}
private resetForm() {
@ -190,7 +188,7 @@ export class AutocompleteComponent implements ControlValueAccessor, OnDestroy, O
}
private reset() {
this.items = [];
this.selectedIndex = -1;
this.suggestedItems = [];
this.suggestedIndex = -1;
}
}

46
src/Squidex/app/framework/angular/forms/tag-editor.component.html

@ -1,18 +1,30 @@
<div class="form-control {{class}}" (click)="input.focus()" [class.focus]="hasFocus" [class.disabled]="addInput.disabled">
<span class="item" *ngFor="let item of items; let i = index" [class.disabled]="addInput.disabled">
{{item}} <i class="icon-close" (click)="remove(i)"></i>
</span>
<ng-container>
<div class="form-control {{class}}" #form (click)="input.focus()" [class.focus]="hasFocus" [class.disabled]="addInput.disabled">
<span class="item" *ngFor="let item of items; let i = index" [class.disabled]="addInput.disabled">
{{item}} <i class="icon-close" (click)="remove(i)"></i>
</span>
<input type="text" class="blank" #input
(blur)="markTouched()"
(focus)="focus()"
(input)="adjustSize()"
(keydown)="onKeyDown($event)"
[formControl]="addInput"
[attr.name]="inputName"
[attr.placeholder]="placeholder"
[class.hidden]="addInput.disabled"
autocomplete="off"
autocorrect="off"
autocapitalize="off">
</div>
<input type="text" class="blank" #input
(blur)="markTouched()"
(focus)="focus()"
(keydown)="onKeyDown($event)"
[formControl]="addInput"
[attr.name]="inputName"
[attr.placeholder]="placeholder"
[class.hidden]="addInput.disabled"
autocomplete="off"
autocorrect="off"
autocapitalize="off">
</div>
<div *ngIf="suggestedItems.length > 0" [sqxModalTarget]="form" class="control-dropdown" #container position="bottomLeft">
<div *ngFor="let item of suggestedItems; let i = index;" class="control-dropdown-item control-dropdown-item-selectable"
[class.active]="i === suggestedIndex"
[container]="container"
(mousedown)="selectValue(item)"
(mouseover)="selectIndex(i)"
[sqxScrollActive]="i === suggestedIndex">
<ng-container>{{item}}</ng-container>
</div>
</div>
</ng-container>

7
src/Squidex/app/framework/angular/forms/tag-editor.component.scss

@ -43,19 +43,14 @@
.item {
& {
@include border-radius(10px);
@include truncate;
display: inline-block;
color: $color-dark-foreground;
cursor: default;
height: 20px;
padding: 0 .6rem;
background: $color-theme-blue;
border: 0;
font-size: .8rem;
font-weight: normal;
line-height: 20px;
margin: 2px 2px 2px 0;
vertical-align: middle;
margin: 0px 2px 2px 0;
}
&,

132
src/Squidex/app/framework/angular/forms/tag-editor.component.ts

@ -5,14 +5,18 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core';
import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subscription } from 'rxjs';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { Types } from '@app/framework/internal';
const KEY_COMMA = 188;
const KEY_DELETE = 8;
const KEY_ENTER = 13;
const KEY_UP = 38;
const KEY_DOWN = 40;
export interface Converter {
convert(input: string): any;
@ -73,7 +77,8 @@ export const SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
templateUrl: './tag-editor.component.html',
providers: [SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR]
})
export class TagEditorComponent implements ControlValueAccessor {
export class TagEditorComponent implements ControlValueAccessor, OnDestroy, OnInit {
private subscription: Subscription;
private callChange = (v: any) => { /* NOOP */ };
private callTouched = () => { /* NOOP */ };
@ -81,11 +86,17 @@ export class TagEditorComponent implements ControlValueAccessor {
public converter: Converter = new StringConverter();
@Input()
public useDefaultValue = true;
public undefinedWhenEmpty = true;
@Input()
public acceptEnter = false;
@Input()
public allowDuplicates = true;
@Input()
public suggestions: string[] = [];
@Input()
public class: string;
@ -100,10 +111,44 @@ export class TagEditorComponent implements ControlValueAccessor {
public hasFocus = false;
public suggestedItems: string[] = [];
public suggestedIndex = 0;
public items: any[] = [];
public addInput = new FormControl();
public ngOnDestroy() {
this.subscription.unsubscribe();
}
public ngOnInit() {
this.subscription =
this.addInput.valueChanges.pipe(
tap(() => {
this.adjustSize();
}),
map(query => <string>query),
map(query => query ? query.trim() : query),
tap(query => {
if (!query) {
this.resetAutocompletion();
}
}),
distinctUntilChanged(),
map(query => {
if (Types.isArray(this.suggestions) && query && query.length > 0) {
return this.suggestions.filter(s => s.indexOf(query) >= 0 && this.items.indexOf(s) < 0);
} else {
return [];
}
}))
.subscribe(items => {
this.suggestedIndex = -1;
this.suggestedItems = items || [];
});
}
public writeValue(obj: any) {
this.resetForm();
@ -136,12 +181,6 @@ export class TagEditorComponent implements ControlValueAccessor {
}
}
private resetForm() {
this.addInput.reset();
this.adjustSize();
}
public markTouched() {
this.callTouched();
@ -171,17 +210,13 @@ export class TagEditorComponent implements ControlValueAccessor {
}
public onKeyDown(event: KeyboardEvent) {
if (event.keyCode === KEY_COMMA || (event.keyCode === KEY_ENTER && this.acceptEnter)) {
const value = <string>this.addInput.value;
if (value && this.converter.isValidInput(value)) {
const converted = this.converter.convert(value);
const key = event.keyCode;
this.updateItems([...this.items, converted]);
this.resetForm();
if (key === KEY_COMMA) {
if (this.selectValue(this.addInput.value)) {
return false;
}
} else if (event.keyCode === KEY_DELETE) {
} else if (key === KEY_DELETE) {
const value = <string>this.addInput.value;
if (!value || value.length === 0) {
@ -189,15 +224,74 @@ export class TagEditorComponent implements ControlValueAccessor {
return false;
}
} else if (key === KEY_UP) {
this.up();
return false;
} else if (key === KEY_DOWN) {
this.down();
return false;
} else if (key === KEY_ENTER) {
if (this.suggestedIndex >= 0) {
if (this.selectValue(this.suggestedItems[this.suggestedIndex])) {
return false;
}
} else if (this.acceptEnter) {
if (this.selectValue(this.addInput.value)) {
return false;
}
}
}
return true;
}
private updateItems(items: string[]) {
public selectValue(value: string) {
if (value && this.converter.isValidInput(value)) {
const converted = this.converter.convert(value);
if (this.allowDuplicates || this.items.indexOf(converted) < 0) {
this.updateItems([...this.items, converted]);
}
this.resetForm();
this.resetAutocompletion();
return true;
}
}
private resetAutocompletion() {
this.suggestedItems = [];
this.suggestedIndex = -1;
}
public selectIndex(selection: number) {
if (selection < 0) {
selection = 0;
}
if (selection >= this.items.length) {
selection = this.items.length - 1;
}
this.suggestedIndex = selection;
}
private resetForm() {
this.addInput.reset();
}
private up() {
this.selectIndex(this.suggestedIndex - 1);
}
private down() {
this.selectIndex(this.suggestedIndex + 1);
}
private updateItems(items: any[]) {
this.items = items;
if (items.length === 0 && this.useDefaultValue) {
if (items.length === 0 && this.undefinedWhenEmpty) {
this.callChange(undefined);
} else {
this.callChange(this.items);

6
src/Squidex/app/framework/angular/http/http-extensions.ts

@ -67,7 +67,11 @@ export const pretifyError = (message: string) => <T>(source: Observable<T>) =>
if (!Types.is(response.error, Error)) {
try {
const errorDto = Types.isObject(response.error) ? response.error : JSON.parse(response.error);
let errorDto = Types.isObject(response.error) ? response.error : JSON.parse(response.error);
if (!errorDto) {
errorDto = { message: 'Failed to make the request.', details: [] };
}
if (response.status === 412) {
result = new ErrorDto(response.status, 'Failed to make the update. Another user has made a change. Please reload.');

8
src/Squidex/app/shared/components/asset.component.html

@ -68,7 +68,13 @@
</div>
</div>
<div class="file-tags tags">
<sqx-tag-editor [acceptEnter]="true" [useDefaultValue]="false" [formControl]="tagInput" class="blank"></sqx-tag-editor>
<sqx-tag-editor
[suggestions]="allTags"
[acceptEnter]="true"
[allowDuplicates]="false"
[undefinedWhenEmpty]="false"
[formControl]="tagInput" class="blank">
</sqx-tag-editor>
</div>
<div class="file-info">
<ng-container *ngIf="asset.pixelWidth">{{asset.pixelWidth}}x{{asset.pixelHeight}}px, </ng-container> {{asset.fileSize | sqxFileSize}}

3
src/Squidex/app/shared/components/asset.component.ts

@ -54,6 +54,9 @@ export class AssetComponent implements OnDestroy, OnInit {
@Input()
public isSelectable = false;
@Input()
public allTags: string[];
@Output()
public loaded = new EventEmitter<AssetDto>();

3
src/Squidex/app/shared/components/assets-list.component.html

@ -14,7 +14,7 @@
<div class="file-drop-info">Drop file on existing item to replace the asset with a newer version.</div>
</div>
<div class="row assets">
<div class="row assets" *ngIf="state.tagsNames | async; let tags">
<sqx-asset *ngFor="let file of newFiles" [initFile]="file"
(failed)="remove(file)"
(loaded)="add(file, $event)">
@ -25,6 +25,7 @@
[isDisabled]="isDisabled"
[isSelectable]="selectedIds"
[isSelected]="isSelected(asset)"
[allTags]="tags"
(updated)="update($event)"
(selected)="select($event)"
(deleting)="delete($event)">

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

@ -37,6 +37,10 @@ export class AssetsState extends State<Snapshot> {
this.changes.pipe(map(x => x.tags),
distinctUntilChanged(), map(x => sort(x)));
public tagsNames =
this.tags.pipe(
distinctUntilChanged(), map(x => x.map(t => t.name)));
public assets =
this.changes.pipe(map(x => x.assets),
distinctUntilChanged());

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

@ -6,14 +6,16 @@
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { Observable, throwError } from 'rxjs';
import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators';
import {
DialogService,
ErrorDto,
ImmutableArray,
notify,
State,
Types,
Version
} from '@app/framework';
@ -100,6 +102,13 @@ export class ContributorsState extends State<Snapshot> {
this.replaceContributors(contributors, dto.version);
}),
catchError(error => {
if (Types.is(error, ErrorDto) && error.statusCode === 404) {
return throwError(new ErrorDto(404, 'The user does not exist.'));
} else {
return throwError(error);
}
}),
notify(this.dialogs));
}

1
src/Squidex/package.json

@ -34,6 +34,7 @@
"moment": "2.22.2",
"mousetrap": "1.6.2",
"ng2-dnd": "5.0.2",
"npm": "^6.2.0",
"oidc-client": "1.4.1",
"pikaday": "1.7.0",
"progressbar.js": "1.0.1",

Loading…
Cancel
Save