diff --git a/src/app/features/model-3d-viewer/model-3d-viewer.component.spec.ts b/src/app/features/model-3d-viewer/model-3d-viewer.component.spec.ts index 48e42620f2..08a17356da 100644 --- a/src/app/features/model-3d-viewer/model-3d-viewer.component.spec.ts +++ b/src/app/features/model-3d-viewer/model-3d-viewer.component.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { TestBed, waitForAsync } from '@angular/core/testing'; import { MysqlQueryService } from '@keira-shared/services/mysql-query.service'; import { ModalModule } from 'ngx-bootstrap/modal'; -import { of, Subscription } from 'rxjs'; +import { of } from 'rxjs'; import { Model3DViewerComponent } from './model-3d-viewer.component'; import { CONTENT_WOTLK, MODEL_TYPE, VIEWER_TYPE } from './model-3d-viewer.model'; @@ -21,9 +21,12 @@ describe('Model3DViewerComponent', () => { const component = fixture.componentInstance; const queryService = TestBed.inject(MysqlQueryService); const httpTestingController = TestBed.inject(HttpTestingController); + const setupViewer3DSpy = spyOn(component, 'setupViewer3D').and.callFake(() => {}); fixture.detectChanges(); + setupViewer3DSpy.calls.reset(); + return { fixture, component, queryService, httpTestingController }; } @@ -36,6 +39,7 @@ describe('Model3DViewerComponent', () => { expect(component['resetModel3dElement']).toHaveBeenCalledTimes(1); expect(component['viewerDynamic']).toHaveBeenCalledTimes(1); + expect(component['setupViewer3D']).toHaveBeenCalledTimes(1); }); it('ngOnChanges', () => { @@ -53,12 +57,13 @@ describe('Model3DViewerComponent', () => { it('ngOnDestroy', () => { const { component } = setup(); - component['subscriptions'] = new Subscription(); const unsubscribeSpy = spyOn(component['subscriptions'], 'unsubscribe'); + spyOn(component, 'resetModel3dElement'); component.ngOnDestroy(); expect(unsubscribeSpy).toHaveBeenCalledTimes(1); + expect(component['resetModel3dElement']).toHaveBeenCalledTimes(1); }); describe('show3Dmodel', () => { @@ -75,7 +80,6 @@ describe('Model3DViewerComponent', () => { it('handles the item 3D model', (done) => { const { component } = setup(); component.viewerType = VIEWER_TYPE.ITEM; - component['subscriptions'] = new Subscription(); const subscriptionAddSpy = spyOn(component['subscriptions'], 'add'); const mockItemData$ = of([{ entry: 123 }]); spyOn(component, 'getItemData$').and.returnValue(mockItemData$); @@ -181,4 +185,13 @@ describe('Model3DViewerComponent', () => { expect(component['getModelType']).toHaveBeenCalledOnceWith(2, 2); }); }); + + it('clean3DModels', () => { + const { component } = setup(); + component['models3D'].push({ destroy: jasmine.createSpy('destroy') }); + + component['clean3DModels'](); + + expect(component['models3D'][0].destroy).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/app/features/model-3d-viewer/model-3d-viewer.component.ts b/src/app/features/model-3d-viewer/model-3d-viewer.component.ts index b38e58285b..855cf10de4 100644 --- a/src/app/features/model-3d-viewer/model-3d-viewer.component.ts +++ b/src/app/features/model-3d-viewer/model-3d-viewer.component.ts @@ -4,10 +4,12 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { MysqlQueryService } from '@keira-shared/services/mysql-query.service'; import { TableRow } from '@keira-shared/types/general'; import * as jquery from 'jquery'; -import { BehaviorSubject, catchError, filter, Observable, of, Subscription } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, catchError, filter, of } from 'rxjs'; import { generateModels, getShadowlandDisplayId } from './helper'; import { CONTENT_LIVE, CONTENT_WOTLK, MODEL_TYPE, VIEWER_TYPE } from './model-3d-viewer.model'; +declare const ZamModelViewer: any; + @Component({ selector: 'keira-model-3d-viewer', templateUrl: './model-3d-viewer.component.html', @@ -19,17 +21,17 @@ export class Model3DViewerComponent implements OnInit, OnDestroy, OnChanges { @Input() itemInventoryType?: number; @Input() id? = 'model_3d'; - private loadedViewer$ = new BehaviorSubject(false); - private subscriptions = new Subscription(); + private readonly loadedViewer$ = new BehaviorSubject(false); + private readonly subscriptions = new Subscription(); + private readonly models3D = []; /* istanbul ignore next */ // because of: https://github.com/gotwarlost/istanbul/issues/690 - constructor(private readonly sanitizer: DomSanitizer, private readonly queryService: MysqlQueryService, private http: HttpClient) { - this.setupViewer3D(); - } + constructor(private readonly sanitizer: DomSanitizer, private readonly queryService: MysqlQueryService, private http: HttpClient) {} public itemPreview: SafeHtml = this.sanitizer.bypassSecurityTrustHtml('loading...'); ngOnInit(): void { + this.setupViewer3D(); this.resetModel3dElement(); this.viewerDynamic(); } @@ -98,6 +100,7 @@ export class Model3DViewerComponent implements OnInit, OnDestroy, OnChanges { contentPath: string = CONTENT_WOTLK, ): void { this.resetModel3dElement(); + generateModels( 1, `#${this.id}`, @@ -106,7 +109,9 @@ export class Model3DViewerComponent implements OnInit, OnDestroy, OnChanges { id: displayId, }, contentPath, - ); + ).then((WoWModel) => { + this.models3D.push(WoWModel); + }); } private getContentPathUrl(inventoryType: number | string): string { @@ -157,9 +162,11 @@ export class Model3DViewerComponent implements OnInit, OnDestroy, OnChanges { const loadedViewer$ = this.loadedViewer$; - jquery.getScript(`${CONTENT_WOTLK}viewer/viewer.min.js`, function () { - loadedViewer$.next(true); - }); + if (typeof ZamModelViewer === 'undefined') { + jquery.getScript(`${CONTENT_WOTLK}viewer/viewer.min.js`, function () { + loadedViewer$.next(true); + }); + } } private viewerDynamic(): void { @@ -173,12 +180,21 @@ export class Model3DViewerComponent implements OnInit, OnDestroy, OnChanges { /* istanbul ignore next */ private resetModel3dElement(): void { const modelElement = document.querySelector(`#${this.id}`); + this.clean3DModels(); if (modelElement) { modelElement.innerHTML = ''; } } + private clean3DModels(): void { + for (let i = 0; i < this.models3D.length; i++) { + this.models3D[i]?.destroy(); + } + delete window['models']; + } + ngOnDestroy(): void { this.subscriptions.unsubscribe(); + this.resetModel3dElement(); } }