Browse Source

Temp

pull/479/head
Sebastian 6 years ago
parent
commit
2cb6ef66e4
  1. 10
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  2. 5
      frontend/app/framework/angular/forms/progress-bar.component.ts
  3. 4
      frontend/app/framework/angular/http/http-extensions.ts
  4. 18
      frontend/app/shared/components/assets/asset-dialog.component.html
  5. 12
      frontend/app/shared/components/assets/asset-dialog.component.scss
  6. 101
      frontend/app/shared/components/assets/asset-dialog.component.ts
  7. 1
      frontend/app/shared/components/assets/asset.component.html
  8. 11
      frontend/app/shared/components/assets/asset.component.ts
  9. 33
      frontend/app/shared/components/assets/image-editor.component.ts
  10. 4
      frontend/app/shared/services/assets.service.ts
  11. 12
      frontend/app/shared/state/asset-uploader.state.ts
  12. 6
      frontend/app/shared/state/assets.forms.ts

10
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
}
}
await UploadAsync(context, tempFile, createAsset, true, next);
await UploadAsync(context, tempFile, createAsset, createAsset.Tags, true, next);
}
finally
{
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
try
{
await UploadAsync(context, tempFile, updateAsset, false, next);
await UploadAsync(context, tempFile, updateAsset, null, false, next);
}
finally
{
@ -107,9 +107,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
}
}
private async Task UploadAsync(CommandContext context, string tempFile, UploadAssetCommand command, bool created, NextDelegate next)
private async Task UploadAsync(CommandContext context, string tempFile, UploadAssetCommand command, HashSet<string>? tags, bool created, NextDelegate next)
{
await EnrichWithMetadataAsync(command);
await EnrichWithMetadataAsync(command, tags);
var asset = await HandleCoreAsync(context, created, next);
@ -160,7 +160,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
}
}
private async Task EnrichWithMetadataAsync(UploadAssetCommand command, HashSet<string>? tags = null)
private async Task EnrichWithMetadataAsync(UploadAssetCommand command, HashSet<string>? tags)
{
foreach (var metadataSource in assetMetadataSources)
{

5
frontend/app/framework/angular/forms/progress-bar.component.ts

@ -11,6 +11,11 @@ import * as ProgressBar from 'progressbar.js';
@Component({
selector: 'sqx-progress-bar',
styles: [`
:host /deep/ svg {
vertical-align: top
}`
],
template: '',
changeDetection: ChangeDetectionStrategy.OnPush
})

4
frontend/app/framework/angular/http/http-extensions.ts

@ -17,7 +17,7 @@ import {
} from '@app/framework/internal';
export module HTTP {
export function upload<T = any>(http: HttpClient, method: string, url: string, file: File, version?: Version): Observable<HttpEvent<T>> {
export function upload<T = any>(http: HttpClient, method: string, url: string, file: Blob, version?: Version): Observable<HttpEvent<T>> {
const req = new HttpRequest(method, url, getFormData(file), { headers: createHeaders(version), reportProgress: true });
return http.request<T>(req);
@ -59,7 +59,7 @@ export module HTTP {
return handleVersion(http.request<T>(method, url, { observe: 'response', headers, body }));
}
function getFormData(file: File) {
function getFormData(file: Blob) {
const formData = new FormData();
formData.append('file', file);

18
frontend/app/shared/components/assets/asset-dialog.component.html

@ -11,7 +11,14 @@
</li>
</ul>
<button type="submit" class="float-right btn btn-primary" [disabled]="!isEditable">Save</button>
<ng-container [ngSwitch]="selectedTab">
<ng-container *ngSwitchCase="'Image'">
<button type="submit" class="float-right btn btn-primary" [class.invisible]="!isUploadable" [disabled]="progress > 0">Save</button>
</ng-container>
<ng-container *ngSwitchCase="'Metadata'">
<button type="submit" class="float-right btn btn-primary" [class.invisible]="!isEditable">Save</button>
</ng-container>
</ng-container>
</ng-container>
<ng-container content>
@ -19,6 +26,15 @@
<ng-container *ngSwitchCase="'Image'">
<div class="image">
<sqx-image-editor [imageUrl]="asset.contentUrl"></sqx-image-editor>
<div class="image-progress" *ngIf="progress > 0">
<sqx-progress-bar
[strokeWidth]="2"
[trailColor]="'transparent'"
[trailWidth]="0"
[value]="progress">
</sqx-progress-bar>
</div>
</div>
</ng-container>
<ng-container *ngSwitchCase="'Metadata'">

12
frontend/app/shared/components/assets/asset-dialog.component.scss

@ -3,7 +3,17 @@
}
.image {
@include absolute(0, 0, 0, 0);
& {
@include absolute(0, 0, 0, 0);
}
&-progress {
@include absolute(0, 0, null, 0);
}
}
.invisible {
visibility: hidden;
}
.metadata {

101
frontend/app/shared/components/assets/asset-dialog.component.ts

@ -5,15 +5,21 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
AnnotateAssetForm,
AssetDto,
AssetsState
AssetsState,
AssetUploaderState,
DialogService,
Types,
UploadCanceled
} from '@app/shared/internal';
import { ImageEditorComponent } from './image-editor.component';
@Component({
selector: 'sqx-asset-dialog',
styleUrls: ['./asset-dialog.component.scss'],
@ -24,30 +30,52 @@ export class AssetDialogComponent implements OnInit {
@Output()
public complete = new EventEmitter();
@Output()
public changed = new EventEmitter<AssetDto>();
@Input()
public asset: AssetDto;
@Input()
public allTags: ReadonlyArray<string>;
@ViewChildren(ImageEditorComponent)
public imageEditor: QueryList<ImageEditorComponent>;
public isEditable = false;
public isEditableAny = false;
public isUploadable = false;
public progress = 0;
public selectableTabs: ReadonlyArray<string> = ['Image', 'Metadata'];
public selectedTab = this.selectableTabs[0];
public selectableTabs: ReadonlyArray<string>;
public selectedTab: string;
public annotateForm = new AnnotateAssetForm(this.formBuilder);
constructor(
private readonly assetsState: AssetsState,
private readonly assetUploader: AssetUploaderState,
private readonly changeDetector: ChangeDetectorRef,
private readonly dialogs: DialogService,
private readonly formBuilder: FormBuilder
) {
}
public ngOnInit() {
this.isEditable = this.asset.canUpdate;
this.isUploadable = this.asset.canUpload;
this.annotateForm.load(this.asset);
this.annotateForm.setEnabled(this.isEditable);
if (this.asset.type === 'Image') {
this.selectableTabs = ['Image', 'Metadata'];
} else {
this.selectableTabs = ['Metadata'];
}
this.selectTab(this.selectableTabs[0]);
}
public selectTab(tab: string) {
@ -63,21 +91,58 @@ export class AssetDialogComponent implements OnInit {
}
public annotateAsset() {
if (!this.isEditable) {
return;
if (this.selectedTab === 'Image') {
if (!this.isUploadable) {
return;
}
const file = this.imageEditor.first.toFile();
if (file) {
this.setProgress(0);
this.assetUploader.uploadAsset(this.asset, file)
.subscribe(dto => {
if (Types.isNumber(dto)) {
this.setProgress(dto);
} else {
this.changed.emit(dto);
}
this.dialogs.notifyInfo('Asset has been updated.');
}, error => {
if (!Types.is(error, UploadCanceled)) {
this.dialogs.notifyError(error);
}
}, () => {
this.setProgress(0);
});
} else {
this.dialogs.notifyInfo('Nothing has changed.');
}
} else {
if (!this.isEditable) {
return;
}
const value = this.annotateForm.submit(this.asset);
if (value) {
this.assetsState.updateAsset(this.asset, value)
.subscribe(() => {
this.annotateForm.submitCompleted({ noReset: true });
this.dialogs.notifyInfo('Asset has been updated.');
}, error => {
this.annotateForm.submitFailed(error);
});
}
}
}
const value = this.annotateForm.submit(this.asset);
if (value) {
this.assetsState.updateAsset(this.asset, value)
.subscribe(() => {
this.emitComplete();
}, error => {
this.annotateForm.submitFailed(error);
});
} else if (this.annotateForm.form.valid) {
this.emitComplete();
}
public setProgress(progress: number) {
this.progress = progress;
this.changeDetector.markForCheck();
}
}

1
frontend/app/shared/components/assets/asset.component.html

@ -153,6 +153,7 @@
<ng-container *ngIf="asset">
<ng-container *sqxModal="editDialog">
<sqx-asset-dialog [allTags]="allTags" [asset]="asset"
(changed)="setAsset($event)"
(complete)="editDialog.hide()">
</sqx-asset-dialog>
</ng-container>

11
frontend/app/shared/components/assets/asset.component.ts

@ -115,8 +115,7 @@ export class AssetComponent implements OnInit {
this.setProgress(asset);
} else {
this.setProgress(0);
this.asset = asset;
this.setAsset(asset);
}
}, error => {
this.dialogs.notifyError(error);
@ -142,7 +141,13 @@ export class AssetComponent implements OnInit {
this.loadError.emit(error);
}
private setProgress(progress: number) {
public setAsset(asset: AssetDto) {
this.asset = asset;
this.changeDetector.markForCheck();
}
public setProgress(progress: number) {
this.progress = progress;
this.changeDetector.markForCheck();

33
frontend/app/shared/components/assets/image-editor.component.ts

@ -102,6 +102,8 @@ const blackTheme = {
})
export class ImageEditorComponent implements AfterViewInit, OnChanges {
private imageEditor: any;
private isChanged = false;
private isChangedBefore = false;
@Input()
public imageUrl: string;
@ -120,6 +122,29 @@ export class ImageEditorComponent implements AfterViewInit, OnChanges {
}
}
public toFile(): Blob | null {
if (!this.isChanged) {
return null;
}
this.isChanged = false;
const dataURI = this.imageEditor.toDataURL();
const byteString = atob(dataURI.split(',')[1]);
const byteBuffer = new ArrayBuffer(byteString.length);
const type = dataURI.split(',')[0].split(':')[1].split(';')[0];
const array = new Uint8Array(byteBuffer);
for (let i = 0; i < byteString.length; i++) {
array[i] = byteString.charCodeAt(i);
}
return new Blob([array], { type });
}
public ngAfterViewInit() {
const styles = [
'https://uicdn.toast.com/tui-color-picker/latest/tui-color-picker.css',
@ -154,6 +179,14 @@ export class ImageEditorComponent implements AfterViewInit, OnChanges {
theme: blackTheme
}
});
this.imageEditor.on('undoStackChanged', () => {
if (this.isChangedBefore) {
this.isChanged = true;
} else {
this.isChangedBefore = true;
}
});
});
}
}

4
frontend/app/shared/services/assets.service.ts

@ -233,7 +233,7 @@ export class AssetsService {
pretifyError('Failed to load assets. Please reload.'));
}
public postAssetFile(appName: string, file: File, parentId?: string): Observable<number | AssetDto> {
public postAssetFile(appName: string, file: Blob, parentId?: string): Observable<number | AssetDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets?parentId=${parentId}`);
return HTTP.upload(this.http, 'POST', url, file).pipe(
@ -266,7 +266,7 @@ export class AssetsService {
pretifyError('Failed to upload asset. Please reload.'));
}
public putAssetFile(appName: string, resource: Resource, file: File, version: Version): Observable<number | AssetDto> {
public putAssetFile(appName: string, resource: Resource, file: Blob, version: Version): Observable<number | AssetDto> {
const link = resource._links['upload'];
const url = this.apiUrl.buildUrl(link.href);

12
frontend/app/shared/state/asset-uploader.state.ts

@ -75,9 +75,9 @@ export class AssetUploaderState extends State<Snapshot> {
const stream = this.assetsService.postAssetFile(this.appName, file, parentId);
return this.upload(stream, MathHelper.guid(), file, asset => {
return this.upload(stream, MathHelper.guid(), file.name, asset => {
if (asset.isDuplicate) {
this.dialogs.notifyError('Asset has already been uploaded.');
this.dialogs.notifyInfo('Asset has already been uploaded.');
} else if (target) {
target.addAsset(asset);
}
@ -86,14 +86,14 @@ export class AssetUploaderState extends State<Snapshot> {
});
}
public uploadAsset(asset: AssetDto, file: File): Observable<UploadResult> {
public uploadAsset(asset: AssetDto, file: Blob): Observable<UploadResult> {
const stream = this.assetsService.putAssetFile(this.appName, asset, file, asset.version);
return this.upload(stream, asset.id, file);
return this.upload(stream, asset.id, file['name'] || asset.fileName);
}
private upload(source: Observable<number | AssetDto>, id: string, file: File, complete?: ((completion: AssetDto) => AssetDto)) {
let upload = { id, name: file.name, progress: 1, status: 'Running', cancel: new Subject() };
private upload(source: Observable<number | AssetDto>, id: string, name: string, complete?: ((completion: AssetDto) => AssetDto)) {
let upload = { id, name, progress: 1, status: 'Running', cancel: new Subject() };
this.addUpload(upload);

6
frontend/app/shared/state/assets.forms.ts

@ -42,11 +42,7 @@ export class AnnotateAssetForm extends Form<FormGroup, AnnotateAssetDto, AssetDt
Validators.required
]
],
tags: [[],
[
Validators.required
]
],
tags: [],
metadata: formBuilder.array([])
}));
}

Loading…
Cancel
Save