From d916b4be6189fb764a6e4a92aa0999a88d71f40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Fri, 9 Jul 2021 12:39:36 +0200 Subject: [PATCH] feat(resource): upload pdf document (DSP-1776) (#481) * feat(resource): display supported file types in upload form * feat(resource): upload document (pdf) * chore(deps): bump dsp-js to v2.6.0 * fix(test): bug fix in tests * refactor(resource): set correct switch cases * fix(resource): fix stupid bug * refactor(lint): make ts-lint happy --- package-lock.json | 14 ++-- package.json | 2 +- src/app/app.module.ts | 2 + src/app/main/pipes/split.pipe.spec.ts | 8 ++ src/app/main/pipes/split.pipe.ts | 22 +++++ .../upload/upload.component.html | 34 +++----- .../upload/upload.component.scss | 2 +- .../upload/upload.component.spec.ts | 6 +- .../representation/upload/upload.component.ts | 80 +++++++++++++++---- .../resource-instance-form.component.ts | 19 ++++- 10 files changed, 140 insertions(+), 49 deletions(-) create mode 100644 src/app/main/pipes/split.pipe.spec.ts create mode 100644 src/app/main/pipes/split.pipe.ts diff --git a/package-lock.json b/package-lock.json index 3bf9224b8d..44264e1ad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@angular/platform-browser-dynamic": "^11.2.9", "@angular/router": "^11.2.9", "@ckeditor/ckeditor5-angular": "^1.2.3", - "@dasch-swiss/dsp-js": "^2.5.0", + "@dasch-swiss/dsp-js": "^2.6.0", "@dasch-swiss/dsp-ui": "^1.6.0", "@ngx-translate/core": "^12.1.2", "@ngx-translate/http-loader": "5.0.0", @@ -2257,9 +2257,9 @@ } }, "node_modules/@dasch-swiss/dsp-js": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-2.5.0.tgz", - "integrity": "sha512-Zd13732lmd/yS/LrfkJj2KJ1TnGWOuXsvffTMXMYpiYtTxdWl90GyFtgbt7IVtdcPyOaGrg8gZpTVssslUSMBw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-2.6.0.tgz", + "integrity": "sha512-l6Aa5jsD8on/QHwH4egom8V9o0bgf2LbibMqpZxw4vgcY/pbxSuCSnOnJw+DwIS+cDUvrDI4GU1eT8TyV1hylw==", "dependencies": { "@types/jsonld": "^1.5.0", "json2typescript": "1.4.1", @@ -22060,9 +22060,9 @@ } }, "@dasch-swiss/dsp-js": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-2.5.0.tgz", - "integrity": "sha512-Zd13732lmd/yS/LrfkJj2KJ1TnGWOuXsvffTMXMYpiYtTxdWl90GyFtgbt7IVtdcPyOaGrg8gZpTVssslUSMBw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@dasch-swiss/dsp-js/-/dsp-js-2.6.0.tgz", + "integrity": "sha512-l6Aa5jsD8on/QHwH4egom8V9o0bgf2LbibMqpZxw4vgcY/pbxSuCSnOnJw+DwIS+cDUvrDI4GU1eT8TyV1hylw==", "requires": { "@types/jsonld": "^1.5.0", "json2typescript": "1.4.1", diff --git a/package.json b/package.json index e55da0dbcb..7648f8c2ab 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@angular/platform-browser-dynamic": "^11.2.9", "@angular/router": "^11.2.9", "@ckeditor/ckeditor5-angular": "^1.2.3", - "@dasch-swiss/dsp-js": "^2.5.0", + "@dasch-swiss/dsp-js": "^2.6.0", "@dasch-swiss/dsp-ui": "^1.6.0", "@ngx-translate/core": "^12.1.2", "@ngx-translate/http-loader": "5.0.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index fcd35e5a44..463fba1dab 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -92,6 +92,7 @@ import { UsersComponent } from './system/users/users.component'; import { UsersListComponent } from './system/users/users-list/users-list.component'; import { VisualizerComponent } from './project/ontology/ontology-visualizer/visualizer/visualizer.component'; import { UploadComponent } from './workspace/resource/representation/upload/upload.component'; +import { SplitPipe } from './main/pipes/split.pipe'; // translate: AoT requires an exported function for factories export function httpLoaderFactory(httpClient: HttpClient) { @@ -171,6 +172,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { UsersListComponent, VisualizerComponent, UploadComponent, + SplitPipe, ], imports: [ AppRoutingModule, diff --git a/src/app/main/pipes/split.pipe.spec.ts b/src/app/main/pipes/split.pipe.spec.ts new file mode 100644 index 0000000000..b540bbe597 --- /dev/null +++ b/src/app/main/pipes/split.pipe.spec.ts @@ -0,0 +1,8 @@ +import { SplitPipe } from './split.pipe'; + +describe('SplitPipe', () => { + it('create an instance', () => { + const pipe = new SplitPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/main/pipes/split.pipe.ts b/src/app/main/pipes/split.pipe.ts new file mode 100644 index 0000000000..a11135cc04 --- /dev/null +++ b/src/app/main/pipes/split.pipe.ts @@ -0,0 +1,22 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +/** + * splits by first argument and returns element from position (second argument) + * + * mystr = application/pdf + * {{ mystr | split: '/':1 }} --> returns 'pdf' + */ +@Pipe({ + name: 'split' +}) +export class SplitPipe implements PipeTransform { + + transform(val: string, separator: string, position: number): string { + return val.split(separator)[position]; + } + + // transform(value: unknown, ...args: unknown[]): unknown { + // return null; + // } + +} diff --git a/src/app/workspace/resource/representation/upload/upload.component.html b/src/app/workspace/resource/representation/upload/upload.component.html index 63d0a7d317..98a29eb584 100644 --- a/src/app/workspace/resource/representation/upload/upload.component.html +++ b/src/app/workspace/resource/representation/upload/upload.component.html @@ -1,33 +1,27 @@
-
- - + + cloud_upload -
Upload file
+

Upload file
+ The following file types are supported:
+ {{ item | split: '/':1 }} +
+

Drag and drop or click to upload
-
- thumbnail -
@@ -48,9 +42,7 @@ {{ convertBytes(file.size) }} {{ convertDate(file.lastModified) }} - diff --git a/src/app/workspace/resource/representation/upload/upload.component.scss b/src/app/workspace/resource/representation/upload/upload.component.scss index f4c75ca248..0e5b78b80b 100644 --- a/src/app/workspace/resource/representation/upload/upload.component.scss +++ b/src/app/workspace/resource/representation/upload/upload.component.scss @@ -28,7 +28,7 @@ font-size: 48px; padding: 0; position: inherit; - top: 35%; + top: 12%; } .title { bottom: 20%; diff --git a/src/app/workspace/resource/representation/upload/upload.component.spec.ts b/src/app/workspace/resource/representation/upload/upload.component.spec.ts index 7f473d7548..d762a5c496 100644 --- a/src/app/workspace/resource/representation/upload/upload.component.spec.ts +++ b/src/app/workspace/resource/representation/upload/upload.component.spec.ts @@ -4,9 +4,10 @@ import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { Constants, CreateStillImageFileValue } from '@dasch-swiss/dsp-js'; +import { CreateStillImageFileValue } from '@dasch-swiss/dsp-js'; import { DspActionModule } from '@dasch-swiss/dsp-ui'; import { of } from 'rxjs'; +import { SplitPipe } from 'src/app/main/pipes/split.pipe'; import { UploadFileService } from './upload-file.service'; import { UploadComponent } from './upload.component'; @@ -49,7 +50,8 @@ describe('UploadComponent', () => { TestBed.configureTestingModule({ declarations: [ UploadComponent, - TestHostComponent + TestHostComponent, + SplitPipe ], imports: [ DspActionModule, diff --git a/src/app/workspace/resource/representation/upload/upload.component.ts b/src/app/workspace/resource/representation/upload/upload.component.ts index c67089c5f1..aa39b66882 100644 --- a/src/app/workspace/resource/representation/upload/upload.component.ts +++ b/src/app/workspace/resource/representation/upload/upload.component.ts @@ -1,13 +1,15 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { + CreateDocumentFileValue, CreateFileValue, CreateStillImageFileValue, + UpdateDocumentFileValue, UpdateFileValue, UpdateStillImageFileValue } from '@dasch-swiss/dsp-js'; import { NotificationService } from '@dasch-swiss/dsp-ui'; -import { UploadedFile, UploadedFileResponse, UploadFileService } from './upload-file.service'; +import { UploadedFileResponse, UploadFileService } from './upload-file.service'; // https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror const resolvedPromise = Promise.resolve(null); @@ -22,7 +24,8 @@ export class UploadComponent implements OnInit { @Input() parentForm?: FormGroup; - @Input() representation: string; // only StillImageRepresentation supported so far + @Input() representation: 'stillImage' | 'movingImage' | 'audio' | 'document' | 'text'; + // only StillImageRepresentation and DocumentPresentation is supported so far @Input() formName: string; @@ -34,8 +37,10 @@ export class UploadComponent implements OnInit { isLoading = false; thumbnailUrl: string; + allowedFileTypes: string[]; // todo: maybe we can use this list to display which file format is allowed to supportedImageTypes = ['image/jpeg', 'image/jp2', 'image/tiff', 'image/tiff-fx', 'image/png']; + supportedDocumentTypes = ['application/pdf']; // readonly fromLabels = { // upload: 'Upload file', @@ -49,6 +54,7 @@ export class UploadComponent implements OnInit { ngOnInit(): void { this.initializeForm(); + this._supportedFileTypes(); } /** @@ -80,14 +86,28 @@ export class UploadComponent implements OnInit { formData.append(this.file.name, this.file); this._upload.upload(formData).subscribe( (res: UploadedFileResponse) => { - const temporaryUrl = res.uploadedFiles[0].temporaryUrl; - const thumbnailUri = '/full/256,/0/default.jpg'; - this.thumbnailUrl = `${temporaryUrl}${thumbnailUri}`; + + switch (this.representation) { + case 'stillImage': + const temporaryUrl = res.uploadedFiles[0].temporaryUrl; + const thumbnailUri = '/full/256,/0/default.jpg'; + this.thumbnailUrl = `${temporaryUrl}${thumbnailUri}`; + break; + + case 'document': + // the preview thumbnail is deactivated for the moment; + // --> TODO: it will be activated as soon as we implement a pdf viewer + // this.thumbnailUrl = res.uploadedFiles[0].temporaryUrl; + this.thumbnailUrl = undefined; + break; + + default: + this.thumbnailUrl = undefined; + break; + } this.fileControl.setValue(res.uploadedFiles[0]); const fileValue = this.getNewValue(); - // console.log('here we should emit the values', res) - // console.log(fileValue); if (fileValue) { this.fileInfo.emit(fileValue); @@ -184,9 +204,22 @@ export class UploadComponent implements OnInit { const filename = this.fileControl.value.internalFilename; - // --> TODO: handle different file types + let fileValue: CreateStillImageFileValue | CreateDocumentFileValue; + + switch (this.representation) { + case 'stillImage': + fileValue = new CreateStillImageFileValue(); + break; + + case 'document': + fileValue = new CreateDocumentFileValue(); + break; + + default: + // --> TODO for UPLOAD: expand with other representation file types + break; + } - const fileValue = new CreateStillImageFileValue(); fileValue.filename = filename; return fileValue; @@ -206,9 +239,23 @@ export class UploadComponent implements OnInit { const filename = this.fileControl.value.internalFilename; - // --> TODO: handle different file types + let fileValue: UpdateStillImageFileValue | UpdateDocumentFileValue; - const fileValue = new UpdateStillImageFileValue(); + + switch (this.representation) { + case 'stillImage': + fileValue = new UpdateStillImageFileValue(); + break; + + case 'document': + fileValue = new UpdateDocumentFileValue(); + break; + default: + // --> TODO for UPLOAD: expand with other representation file types + break; + } + + // const fileValue = new UpdateStillImageFileValue(); fileValue.filename = filename; fileValue.id = id; @@ -228,16 +275,19 @@ export class UploadComponent implements OnInit { * returns supported file types list for certain resource type */ private _supportedFileTypes(): string[] { - let allowedFileTypes: string[]; + this.allowedFileTypes = []; switch (this.representation) { case 'stillImage': - allowedFileTypes = this.supportedImageTypes; + this.allowedFileTypes = this.supportedImageTypes; + break; + case 'document': + this.allowedFileTypes = this.supportedDocumentTypes; break; default: - allowedFileTypes = []; + this.allowedFileTypes = []; break; } - return allowedFileTypes; + return this.allowedFileTypes; } /** diff --git a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts index bdccc78633..14cd628514 100644 --- a/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts +++ b/src/app/workspace/resource/resource-instance-form/resource-instance-form.component.ts @@ -165,7 +165,14 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy { }); if (this.fileValue) { - this.propertiesObj[Constants.HasStillImageFileValue] = [this.fileValue]; + switch (this.hasFileValue) { + case 'stillImage': + this.propertiesObj[Constants.HasStillImageFileValue] = [this.fileValue]; + break; + case 'document': + this.propertiesObj[Constants.HasDocumentFileValue] = [this.fileValue]; + break; + } } createResource.properties = this.propertiesObj; @@ -370,11 +377,19 @@ export class ResourceInstanceFormComponent implements OnInit, OnDestroy { // filter out all props that cannot be edited or are link props but also the hasFileValue props this.properties = onto.getPropertyDefinitionsByType(ResourcePropertyDefinition).filter( - prop => prop.isEditable && !prop.isLinkProperty && prop.id !== Constants.HasStillImageFileValue); + prop => + !prop.isLinkProperty && + prop.isEditable && + prop.id !== Constants.HasStillImageFileValue && + prop.id !== Constants.HasDocumentFileValue // --> TODO for UPLOAD: expand with other representation file values + ); if (onto.properties[Constants.HasStillImageFileValue]) { this.hasFileValue = 'stillImage'; } + if (onto.properties[Constants.HasDocumentFileValue]) { + this.hasFileValue = 'document'; + } // notifies the user that the selected resource does not have any properties defined yet. if (!this.selectPropertiesComponent && this.properties.length === 0) {