diff --git a/src/app/workspace/resource/representation/archive/archive.component.ts b/src/app/workspace/resource/representation/archive/archive.component.ts index 761dd397d2..311615600f 100644 --- a/src/app/workspace/resource/representation/archive/archive.component.ts +++ b/src/app/workspace/resource/representation/archive/archive.component.ts @@ -1,7 +1,17 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; -import { Constants, UpdateFileValue, UpdateResource, UpdateValue, WriteValueResponse, ReadResource, ApiResponseError, KnoraApiConnection, ReadArchiveFileValue } from '@dasch-swiss/dsp-js'; +import { + ApiResponseError, + Constants, + KnoraApiConnection, + ReadArchiveFileValue, + ReadResource, + UpdateFileValue, + UpdateResource, + UpdateValue, + WriteValueResponse +} from '@dasch-swiss/dsp-js'; import { mergeMap } from 'rxjs/operators'; import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens'; import { DialogComponent } from 'src/app/main/dialog/dialog.component'; @@ -14,11 +24,14 @@ import { FileRepresentation } from '../file-representation'; templateUrl: './archive.component.html', styleUrls: ['./archive.component.scss'] }) -export class ArchiveComponent implements OnInit { +export class ArchiveComponent implements OnInit, AfterViewInit { @Input() src: FileRepresentation; + @Input() parentResource: ReadResource; + @Output() loaded = new EventEmitter(); + originalFilename: string; constructor( @@ -33,6 +46,10 @@ export class ArchiveComponent implements OnInit { this._getOriginalFilename(); } + ngAfterViewInit() { + this.loaded.emit(true); + } + // https://stackoverflow.com/questions/66986983/angular-10-download-file-from-firebase-link-without-opening-into-new-tab async downloadArchive(url: string) { try { diff --git a/src/app/workspace/resource/representation/audio/audio.component.ts b/src/app/workspace/resource/representation/audio/audio.component.ts index b75207f3c8..0e3465b58d 100644 --- a/src/app/workspace/resource/representation/audio/audio.component.ts +++ b/src/app/workspace/resource/representation/audio/audio.component.ts @@ -1,25 +1,38 @@ -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { UpdateFileValue, UpdateResource, Constants, UpdateValue, WriteValueResponse, ReadResource, ApiResponseError, KnoraApiConnection, ReadAudioFileValue } from '@dasch-swiss/dsp-js'; +import { + ApiResponseError, + Constants, + KnoraApiConnection, + ReadAudioFileValue, + ReadResource, + UpdateFileValue, + UpdateResource, + UpdateValue, + WriteValueResponse +} from '@dasch-swiss/dsp-js'; import { mergeMap } from 'rxjs/operators'; import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens'; import { DialogComponent } from 'src/app/main/dialog/dialog.component'; import { ErrorHandlerService } from 'src/app/main/error/error-handler.service'; import { EmitEvent, Events, UpdatedFileEventValue, ValueOperationEventService } from '../../services/value-operation-event.service'; - import { FileRepresentation } from '../file-representation'; + @Component({ selector: 'app-audio', templateUrl: './audio.component.html', styleUrls: ['./audio.component.scss'] }) -export class AudioComponent implements OnInit { +export class AudioComponent implements OnInit, AfterViewInit { @Input() src: FileRepresentation; + @Input() parentResource: ReadResource; + @Output() loaded = new EventEmitter(); + audio: SafeUrl; constructor( @@ -34,6 +47,10 @@ export class AudioComponent implements OnInit { this.audio = this._sanitizer.bypassSecurityTrustUrl(this.src.fileValue.fileUrl); } + ngAfterViewInit() { + this.loaded.emit(true); + } + openReplaceFileDialog(){ const propId = this.parentResource.properties[Constants.HasAudioFileValue][0].id; diff --git a/src/app/workspace/resource/representation/document/document.component.ts b/src/app/workspace/resource/representation/document/document.component.ts index 5cd596fa74..50021c1806 100644 --- a/src/app/workspace/resource/representation/document/document.component.ts +++ b/src/app/workspace/resource/representation/document/document.component.ts @@ -1,6 +1,16 @@ -import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Inject, Input, OnInit, Output, ViewChild } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; -import { ApiResponseError, Constants, KnoraApiConnection, ReadDocumentFileValue, ReadResource, UpdateFileValue, UpdateResource, UpdateValue, WriteValueResponse } from '@dasch-swiss/dsp-js'; +import { + ApiResponseError, + Constants, + KnoraApiConnection, + ReadDocumentFileValue, + ReadResource, + UpdateFileValue, + UpdateResource, + UpdateValue, + WriteValueResponse +} from '@dasch-swiss/dsp-js'; import { PdfViewerComponent } from 'ng2-pdf-viewer'; import { mergeMap } from 'rxjs/operators'; import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens'; @@ -14,11 +24,14 @@ import { FileRepresentation } from '../file-representation'; templateUrl: './document.component.html', styleUrls: ['./document.component.scss'] }) -export class DocumentComponent implements OnInit { +export class DocumentComponent implements OnInit, AfterViewInit { @Input() src: FileRepresentation; + @Input() parentResource: ReadResource; + @Output() loaded = new EventEmitter(); + @ViewChild(PdfViewerComponent) private _pdfComponent: PdfViewerComponent; zoomFactor = 1.0; @@ -36,6 +49,10 @@ export class DocumentComponent implements OnInit { } + ngAfterViewInit() { + this.loaded.emit(true); + } + searchQueryChanged(newQuery: string) { if (newQuery !== this.pdfQuery) { this.pdfQuery = newQuery; diff --git a/src/app/workspace/resource/representation/still-image/still-image.component.html b/src/app/workspace/resource/representation/still-image/still-image.component.html index b5f2ea2268..18dfcc81be 100644 --- a/src/app/workspace/resource/representation/still-image/still-image.component.html +++ b/src/app/workspace/resource/representation/still-image/still-image.component.html @@ -89,5 +89,5 @@ - + diff --git a/src/app/workspace/resource/representation/still-image/still-image.component.ts b/src/app/workspace/resource/representation/still-image/still-image.component.ts index 1a8446b520..0b7bbe4d2b 100644 --- a/src/app/workspace/resource/representation/still-image/still-image.component.ts +++ b/src/app/workspace/resource/representation/still-image/still-image.component.ts @@ -1,10 +1,13 @@ import { + AfterViewInit, Component, ElementRef, - EventEmitter, Inject, + EventEmitter, + Inject, Input, OnChanges, - OnDestroy, Output, + OnDestroy, + Output, Renderer2, SimpleChanges } from '@angular/core'; @@ -110,7 +113,7 @@ interface PolygonsForRegion { templateUrl: './still-image.component.html', styleUrls: ['./still-image.component.scss'] }) -export class StillImageComponent implements OnChanges, OnDestroy { +export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit { @Input() images: FileRepresentation[]; @Input() imageCaption?: string; @@ -127,7 +130,10 @@ export class StillImageComponent implements OnChanges, OnDestroy { @Output() regionClicked = new EventEmitter(); @Output() regionAdded = new EventEmitter(); - regionDrawMode: Boolean = false; // stores whether viewer is currently drawing a region + + @Output() loaded = new EventEmitter(); + + regionDrawMode = false; // stores whether viewer is currently drawing a region private _regionDragInfo; // stores the information of the first click for drawing a region private _viewer: OpenSeadragon.Viewer; private _regions: PolygonsForRegion = {}; @@ -195,6 +201,10 @@ export class StillImageComponent implements OnChanges, OnDestroy { } } + ngAfterViewInit() { + this.loaded.emit(true); + } + ngOnDestroy() { if (this._viewer) { this._viewer.destroy(); diff --git a/src/app/workspace/resource/representation/video/video.component.ts b/src/app/workspace/resource/representation/video/video.component.ts index b208674469..93a7cb2deb 100644 --- a/src/app/workspace/resource/representation/video/video.component.ts +++ b/src/app/workspace/resource/representation/video/video.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, HostListener, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, EventEmitter, HostListener, Inject, Input, OnInit, Output, ViewChild } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { @@ -16,12 +16,7 @@ import { mergeMap } from 'rxjs/operators'; import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens'; import { DialogComponent } from 'src/app/main/dialog/dialog.component'; import { ErrorHandlerService } from 'src/app/main/error/error-handler.service'; -import { - EmitEvent, - Events, - UpdatedFileEventValue, - ValueOperationEventService -} from '../../services/value-operation-event.service'; +import { EmitEvent, Events, UpdatedFileEventValue, ValueOperationEventService } from '../../services/value-operation-event.service'; import { PointerValue } from '../av-timeline/av-timeline.component'; import { FileRepresentation } from '../file-representation'; @@ -30,7 +25,7 @@ import { FileRepresentation } from '../file-representation'; templateUrl: './video.component.html', styleUrls: ['./video.component.scss'] }) -export class VideoComponent implements OnInit { +export class VideoComponent implements OnInit, AfterViewInit { @Input() src: FileRepresentation; @@ -38,6 +33,8 @@ export class VideoComponent implements OnInit { @Input() parentResource: ReadResource; + @Output() loaded = new EventEmitter(); + @ViewChild('videoEle') videoEle: ElementRef; @ViewChild('timeline') timeline: ElementRef; @@ -116,6 +113,10 @@ export class VideoComponent implements OnInit { this.fileHasChanged = false; } + ngAfterViewInit() { + this.loaded.emit(true); + } + /** * stop playing and go back to start */ diff --git a/src/app/workspace/resource/resource.component.html b/src/app/workspace/resource/resource.component.html index 52f6216818..1c3ded8fdf 100644 --- a/src/app/workspace/resource/resource.component.html +++ b/src/app/workspace/resource/resource.component.html @@ -1,11 +1,11 @@
-
+
- - + - + + - + + - + + The file representation type "{{representationsToDisplay[0].fileValue.type}}" is not yet implemented -
+
- - - - - - - - - - - - - + + + + + + + - - - - - Annotations - - -
- + + + [valueUuidToHighlight]="valueUuid"> -
+
- -
- - - - + + + + + Annotations + + +
+ + +
+ +
+
+ + + + +
+ +
- -
diff --git a/src/app/workspace/resource/resource.component.ts b/src/app/workspace/resource/resource.component.ts index 600ef4996d..9ae86c7721 100644 --- a/src/app/workspace/resource/resource.component.ts +++ b/src/app/workspace/resource/resource.component.ts @@ -209,7 +209,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { this.compoundPosition.position = position; this.compoundPosition.page = page; - this.collectRepresentationsAndAnnotations(this.incomingResource); + this.representationsToDisplay = this.collectRepresentationsAndAnnotations(this.incomingResource); } @@ -254,7 +254,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { this.adminPermissions = this.session.user.sysAdmin ? this.session.user.sysAdmin : this.session.user.projectAdmin.some(e => e === res.res.attachedToProject); } - this.collectRepresentationsAndAnnotations(this.resource); + this.representationsToDisplay = this.collectRepresentationsAndAnnotations(this.resource); if (!this.representationsToDisplay.length && !this.compoundPosition) { // the resource could be a compound object @@ -264,6 +264,9 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { if (countQuery.numberOfResults > 0) { this.compoundPosition = new DspCompoundPosition(countQuery.numberOfResults); this.compoundNavigation(1); + } else { + // not a compound object + this.loading = false; } }, (error: ApiResponseError) => { @@ -280,7 +283,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { // gather system property information res.systemProps = this.resource.res.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition); } - this.loading = false; + // this.loading = false; }, (error: ApiResponseError) => { this.loading = false; @@ -311,7 +314,9 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { res.resProps = this.initProps(response); res.systemProps = this.incomingResource.res.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition); - this.collectRepresentationsAndAnnotations(this.incomingResource); + this.representationsToDisplay = this.collectRepresentationsAndAnnotations(this.incomingResource); + + this.loading = this.representationsToDisplay.length > 0; if (this.representationsToDisplay.length && this.representationsToDisplay[0].fileValue && this.compoundPosition) { this.getIncomingRegions(this.incomingResource, 0); @@ -333,6 +338,10 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { this.selectedTabLabel = e.tab.textLabel; } + representationLoaded(e: boolean) { + this.loading = !e; + } + /** * gather resource property information */ @@ -386,13 +395,13 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { * @param resource The resource to get the images for. * @returns A collection of images for the given resource. */ - protected collectRepresentationsAndAnnotations(resource: DspResource): any { + protected collectRepresentationsAndAnnotations(resource: DspResource): FileRepresentation[] { if (!resource) { return; } - // --> TODO: should be a general object for all kind of representations + // general object for all kind of representations const representations: FileRepresentation[] = []; // --> TODO: use a switch here to go through the different representation types @@ -465,8 +474,10 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { const fileValue: ReadArchiveFileValue = resource.res.properties[Constants.HasArchiveFileValue][0] as ReadArchiveFileValue; const archive = new FileRepresentation(fileValue); representations.push(archive); + } - this.representationsToDisplay = representations; + + return representations; } @@ -498,6 +509,8 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { this.resource.incomingRepresentations = incomingImageRepresentations.resources; this.getIncomingResource(this.resource.incomingRepresentations[this.compoundPosition.position].id); + } else { + this.loading = false; } }, (error: ApiResponseError) => { @@ -509,7 +522,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { /** * requests incoming resources for [[this.resource]]. - * Incoming resources are: regions, StillImageRepresentations, and incoming links. + * Incoming resources are: regions, representations, and incoming links. */ protected requestIncomingResources(resource: DspResource): void { @@ -559,7 +572,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { // prepare regions to be displayed // triggers ngOnChanges of StillImageComponent - this.collectRepresentationsAndAnnotations(resource); + this.representationsToDisplay = this.collectRepresentationsAndAnnotations(resource); }, (error: ApiResponseError) => {