diff --git a/docs/content-services/components/alfresco-viewer.component.md b/docs/content-services/components/alfresco-viewer.component.md index c076234238..a6ec5a49a4 100644 --- a/docs/content-services/components/alfresco-viewer.component.md +++ b/docs/content-services/components/alfresco-viewer.component.md @@ -245,16 +245,17 @@ The Viewer supports dynamically-loaded viewer preview extensions, to know more a You can define your own custom handler to override supported file formats or handle other file formats that are not yet supported by the component. Below is an example that shows how to use the `adf-viewer-extension` -to handle 3D data files: +to handle 3D data files. `contentLoaded` should be an `EventEmitter` that will emit as soon as the component responsible for rendering finishes. ```html - + + [extension]="extension" + (contentLoaded)="markAsLoaded()"> @@ -270,17 +271,19 @@ You need to keep all instances of `adf-viewer-extension` inside `viewerExtension - + + urlFileContent="urlFileContent" + (contentLoaded)="markAsLoaded()"> - + + urlFileContent="urlFileContent" + (contentLoaded)="markAsLoaded()"> diff --git a/docs/core/components/viewer-render.component.md b/docs/core/components/viewer-render.component.md index 743deb404a..27665fb562 100644 --- a/docs/core/components/viewer-render.component.md +++ b/docs/core/components/viewer-render.component.md @@ -206,22 +206,24 @@ The Viewer supports dynamically-loaded viewer preview extensions, to know more a #### Code extension mechanism You can define your own custom handler to override supported file formats or handle other file formats that are not yet supported by -the [Viewer render component](viewer.component.md). In order to do that first you need to define a template containing at least one `adf-viewer-extension`: +the [Viewer render component](viewer.component.md). In order to do that first you need to define a template containing at least one `adf-viewer-extension`. `contentLoaded` should be an `EventEmitter` that will emit as soon as the component responsible for rendering finishes. ```html - + + urlFileContent="urlFileContent" + (contentLoaded)="markAsLoaded()"> - + + urlFileContent="urlFileContent" + (contentLoaded)="markAsLoaded()"> diff --git a/docs/core/components/viewer.component.md b/docs/core/components/viewer.component.md index aaeda2f0bf..62cc43cd5d 100644 --- a/docs/core/components/viewer.component.md +++ b/docs/core/components/viewer.component.md @@ -215,16 +215,17 @@ The Viewer supports dynamically-loaded viewer preview extensions, to know more a You can define your own custom handler to override supported file formats or handle other file formats that are not yet supported by the [Viewer component](viewer.component.md). Below is an example that shows how to use the `adf-viewer-extension` -to handle 3D data files: +to handle 3D data files. `contentLoaded` should be an `EventEmitter` that will emit as soon as the component responsible for rendering finishes. ```html - + + [extension]="extension" + (contentLoaded)="markAsLoaded()"> @@ -239,17 +240,19 @@ You need to keep all instances of `adf-viewer-extension` inside `viewerExtension - + + urlFileContent="urlFileContent" + (contentLoaded)="markAsLoaded()"> - + + urlFileContent="urlFileContent" + (contentLoaded)="markAsLoaded()"> diff --git a/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.spec.ts b/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.spec.ts index 05019a5a5d..9028f62c48 100644 --- a/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.spec.ts +++ b/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.spec.ts @@ -42,6 +42,7 @@ describe('Text View component', () => { describe('View', () => { it('Should text container be present with urlFile', async () => { + spyOn(component.contentLoaded, 'emit'); fixture.detectChanges(); const urlFile = './fake-test-file.txt'; const change = new SimpleChange(null, urlFile, true); @@ -52,9 +53,11 @@ describe('Text View component', () => { await fixture.whenStable(); expect(testingUtils.getByCSS('.adf-txt-viewer-content').nativeElement.textContent).toContain('example'); + expect(component.contentLoaded.emit).toHaveBeenCalled(); }); it('Should text container be present with Blob file', async () => { + spyOn(component.contentLoaded, 'emit'); const blobFile = new Blob(['text example'], { type: 'text/txt' }); const change = new SimpleChange(null, blobFile, true); @@ -65,6 +68,7 @@ describe('Text View component', () => { await fixture.whenStable(); expect(testingUtils.getByCSS('.adf-txt-viewer-content').nativeElement.textContent).toContain('example'); + expect(component.contentLoaded.emit).toHaveBeenCalled(); }); }); }); diff --git a/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts b/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts index c818c3cd72..91cda5e03c 100644 --- a/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts +++ b/lib/core/src/lib/viewer/components/txt-viewer/txt-viewer.component.ts @@ -16,7 +16,7 @@ */ import { HttpClient } from '@angular/common/http'; -import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { AppConfigService } from '../../../app-config'; @Component({ @@ -34,6 +34,9 @@ export class TxtViewerComponent implements OnChanges { @Input() blobFile: Blob; + @Output() + contentLoaded = new EventEmitter(); + content: string | ArrayBuffer; constructor(private http: HttpClient, private appConfigService: AppConfigService) {} @@ -67,6 +70,9 @@ export class TxtViewerComponent implements OnChanges { }, (event) => { reject(event); + }, + () => { + this.contentLoaded.emit(); } ); }); @@ -78,13 +84,17 @@ export class TxtViewerComponent implements OnChanges { reader.onload = () => { this.content = reader.result; - resolve(); }; reader.onerror = (error: any) => { reject(error); }; + reader.onloadend = () => { + this.contentLoaded.emit(); + resolve(); + }; + reader.readAsText(blob); }); } diff --git a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.html b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.html index 0754ac5464..31b7f67586 100644 --- a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.html +++ b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.html @@ -23,6 +23,7 @@ [extension]="externalViewer.fileExtension" [nodeId]="nodeId" [attr.data-automation-id]="externalViewer.component" + (contentLoaded)="markAsLoaded()" /> @@ -68,7 +69,7 @@ - + @@ -80,6 +81,7 @@ [extension]="extension" [nodeId]="nodeId" [attr.data-automation-id]="ext.component" + (contentLoaded)="markAsLoaded()" /> @@ -87,7 +89,7 @@ @@ -101,5 +103,5 @@ } - + diff --git a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.spec.ts b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.spec.ts index ab14d2d218..61cbc21d94 100644 --- a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.spec.ts +++ b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.spec.ts @@ -520,7 +520,8 @@ describe('ViewerComponent', () => { expect(component.viewerType).toBe('pdf'); }); - it('should show spinner until content is ready when viewerType is image', () => { + it('should show spinner until renderer calls markAsLoaded', () => { + spyOn(component, 'markAsLoaded').and.callThrough(); component.isLoading = false; component.urlFile = 'some-url.png'; @@ -534,16 +535,7 @@ describe('ViewerComponent', () => { expect(getMainLoader()).toBeNull(); expect(component.viewerType).toBe('image'); - }); - - it('should not show spinner when isLoading = false and isContentReady = false for other viewer types', () => { - component.isLoading = false; - component.urlFile = 'some-url.txt'; - - component.ngOnChanges(); - fixture.detectChanges(); - - expect(getMainLoader()).toBeNull(); + expect(component.markAsLoaded).toHaveBeenCalled(); }); }); }); diff --git a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts index 987f3149d9..6df1f1ae47 100644 --- a/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts +++ b/lib/core/src/lib/viewer/components/viewer-render/viewer-render.component.ts @@ -178,10 +178,11 @@ export class ViewerRenderComponent implements OnChanges, OnInit { ngOnInit() { this.cacheTypeForContent = 'no-cache'; - this.setDefaultLoadingState(); + this.isLoading = true; } ngOnChanges() { + this.isLoading = true; if (this.blobFile) { this.setUpBlobData(); } else if (this.urlFile) { @@ -196,6 +197,9 @@ export class ViewerRenderComponent implements OnChanges, OnInit { private setUpBlobData() { this.internalFileName = this.fileName; this.viewerType = this.viewUtilService.getViewerTypeByMimeType(this.blobFile.type); + if (this.viewerType === 'unknown') { + this.isLoading = false; + } this.extensionChange.emit(this.blobFile.type); this.scrollTop(); @@ -205,6 +209,9 @@ export class ViewerRenderComponent implements OnChanges, OnInit { this.internalFileName = this.fileName ? this.fileName : this.viewUtilService.getFilenameFromUrl(this.urlFile); this.extension = this.viewUtilService.getFileExtension(this.internalFileName); this.viewerType = this.viewUtilService.getViewerType(this.extension, this.mimeType, this.extensionsSupportedByTemplates); + if (this.viewerType === 'unknown') { + this.isLoading = false; + } this.extensionChange.emit(this.extension); this.scrollTop(); @@ -233,14 +240,4 @@ export class ViewerRenderComponent implements OnChanges, OnInit { onClose() { this.close.next(true); } - - private canBePreviewed(): boolean { - return this.viewerType === 'media' || this.viewerType === 'pdf' || this.viewerType === 'image'; - } - - private setDefaultLoadingState() { - if (this.canBePreviewed()) { - this.isLoading = true; - } - } } diff --git a/lib/extensions/src/lib/components/viewer/preview-extension.component.ts b/lib/extensions/src/lib/components/viewer/preview-extension.component.ts index 0b38d2c8d6..9924fa70bb 100644 --- a/lib/extensions/src/lib/components/viewer/preview-extension.component.ts +++ b/lib/extensions/src/lib/components/viewer/preview-extension.component.ts @@ -15,8 +15,22 @@ * limitations under the License. */ -import { Component, Input, ComponentRef, OnInit, ViewChild, ViewContainerRef, OnDestroy, OnChanges } from '@angular/core'; +import { + Component, + Input, + ComponentRef, + OnInit, + ViewChild, + ViewContainerRef, + OnDestroy, + OnChanges, + EventEmitter, + Output, + DestroyRef, + inject +} from '@angular/core'; import { ExtensionService } from '../../services/extension.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'adf-preview-extension', @@ -43,6 +57,11 @@ export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { @Input() extension: string; + @Output() + contentLoaded = new EventEmitter(); + + private readonly destroyRef = inject(DestroyRef); + private componentRef: ComponentRef; constructor(private extensionService: ExtensionService) {} @@ -73,11 +92,15 @@ export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { private updateInstance() { if (this.componentRef?.instance) { - const instance = this.componentRef.instance; + this.componentRef.setInput('url', this.url); + this.componentRef.setInput('extension', this.extension); + this.componentRef.setInput('nodeId', this.nodeId); - instance.url = this.url; - instance.extension = this.extension; - instance.nodeId = this.nodeId; + if (this.componentRef.instance?.contentLoaded) { + this.componentRef.instance.contentLoaded.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { + this.contentLoaded.emit(); + }); + } } } }