[#39184] Switching viewer dropdown fails to load (#9763)

* [#39184] Switching viewer dropdown fails to load

- https://community.openproject.org/work_packages/39184
- ifc viewer is now only initialized after view is initialized

* Fix bim nav spec to check for xoekit init

Co-authored-by: Wieland Lindenthal <w.lindenthal@forkmerge.com>
pull/9767/head
Eric Schubert 3 years ago committed by GitHub
parent 2ebe88176e
commit 0ab5b5a164
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      frontend/src/app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.component.html
  2. 78
      frontend/src/app/modules/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts
  3. 6
      modules/bim/spec/features/bim_navigation_spec.rb
  4. 8
      modules/bim/spec/support/pages/ifc_models/show_default.rb

@ -15,12 +15,14 @@
</div> </div>
</div> </div>
<div *ngIf="modelCount" <div #viewerContainer
*ngIf="modelCount"
class="op-ifc-viewer--container xeokit-busy-modal-backdrop" class="op-ifc-viewer--container xeokit-busy-modal-backdrop"
data-qa-selector="op-ifc-viewer--container"> data-qa-selector="op-ifc-viewer--container">
<div class="op-ifc-viewer--toolbar op-ifc-viewer--model-canvas-overlay"> <div class="op-ifc-viewer--toolbar op-ifc-viewer--model-canvas-overlay">
<div class="op-ifc-viewer--toolbar-container" <div #toolbar
class="op-ifc-viewer--toolbar-container"
data-qa-selector="op-ifc-viewer--toolbar-container"> data-qa-selector="op-ifc-viewer--toolbar-container">
</div> </div>
@ -46,11 +48,13 @@
</a> </a>
</div> </div>
<canvas class="op-ifc-viewer--nav-cube-canvas op-ifc-viewer--model-canvas-overlay" <canvas #navCubeCanvas
class="op-ifc-viewer--nav-cube-canvas op-ifc-viewer--model-canvas-overlay"
data-qa-selector="op-ifc-viewer--nav-cube-canvas"></canvas> data-qa-selector="op-ifc-viewer--nav-cube-canvas"></canvas>
</div> </div>
<div class="op-ifc-viewer--inspector-container" <div #inspectorPane
class="op-ifc-viewer--inspector-container"
[class.op-ifc-viewer--inspector-container-hidden]="!(inspectorVisible$ | async)" [class.op-ifc-viewer--inspector-container-hidden]="!(inspectorVisible$ | async)"
data-qa-selector="op-ifc-viewer--inspector-container"> data-qa-selector="op-ifc-viewer--inspector-container">
</div> </div>

@ -27,6 +27,7 @@
//++ //++
import { import {
AfterViewInit,
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef, ElementRef,
@ -40,18 +41,19 @@ import { IfcModelsDataService } from 'core-app/modules/bim/ifc_models/pages/view
import { I18nService } from 'core-app/modules/common/i18n/i18n.service'; import { I18nService } from 'core-app/modules/common/i18n/i18n.service';
import { CurrentUserService } from 'core-app/modules/current-user/current-user.service'; import { CurrentUserService } from 'core-app/modules/current-user/current-user.service';
import { CurrentProjectService } from 'core-app/components/projects/current-project.service'; import { CurrentProjectService } from 'core-app/components/projects/current-project.service';
import { BehaviorSubject } from "rxjs"; import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { take } from "rxjs/operators";
@Component({ @Component({
selector: 'ifc-viewer', selector: 'op-ifc-viewer',
templateUrl: './ifc-viewer.component.html', templateUrl: './ifc-viewer.component.html',
styleUrls: ['./ifc-viewer.component.sass'], styleUrls: ['./ifc-viewer.component.sass'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class IFCViewerComponent implements OnInit, OnDestroy { export class IFCViewerComponent implements OnInit, OnDestroy, AfterViewInit {
private viewerUI:any; private viewInitialized$ = new Subject();
modelCount:number; modelCount:number = this.ifcData.models.length;
canManage = this.ifcData.allowed('manage_ifc_models'); canManage = this.ifcData.allowed('manage_ifc_models');
@ -63,55 +65,67 @@ export class IFCViewerComponent implements OnInit, OnDestroy {
keyboardEnabled = false; keyboardEnabled = false;
public inspectorVisible$:BehaviorSubject<boolean>; inspectorVisible$:BehaviorSubject<boolean> = this.ifcViewerService.inspectorVisible$;
@ViewChild('outerContainer') outerContainer:ElementRef; @ViewChild('outerContainer') outerContainer:ElementRef;
@ViewChild('viewerContainer') viewerContainer:ElementRef;
@ViewChild('modelCanvas') modelCanvas:ElementRef; @ViewChild('modelCanvas') modelCanvas:ElementRef;
@ViewChild('navCubeCanvas') navCubeCanvas:ElementRef;
@ViewChild('toolbar') toolbarElement:ElementRef;
@ViewChild('inspectorPane') inspectorElement:ElementRef;
constructor(private I18n:I18nService, constructor(private I18n:I18nService,
private elementRef:ElementRef, private elementRef:ElementRef,
public ifcData:IfcModelsDataService, public ifcData:IfcModelsDataService,
private ifcViewerService:IFCViewerService, private ifcViewerService:IFCViewerService,
private currentUserService:CurrentUserService, private currentUserService:CurrentUserService,
private currentProjectService:CurrentProjectService) { private currentProjectService:CurrentProjectService) {
this.inspectorVisible$ = this.ifcViewerService.inspectorVisible$;
} }
ngOnInit():void { ngOnInit():void {
this.modelCount = this.ifcData.models.length;
if (this.modelCount === 0) { if (this.modelCount === 0) {
return; return;
} }
const element = jQuery(this.elementRef.nativeElement as HTMLElement); // we have to wait until view is initialized before constructing the ifc viewer,
// as it needs all view children ready and rendered
this.currentUserService combineLatest(
.hasCapabilities$( this.currentUserService
[ .hasCapabilities$(
'ifc_models/create', [
'ifc_models/update', 'ifc_models/create',
'ifc_models/destroy', 'ifc_models/update',
], 'ifc_models/destroy',
this.currentProjectService.id as string, ],
) this.currentProjectService.id as string,
.subscribe((manageIfcModelsAllowed) => { ),
this.viewInitialized$,
)
.pipe(take(1))
.subscribe(([manageIfcModelsAllowed]) => {
this.ifcViewerService.newViewer( this.ifcViewerService.newViewer(
{ {
canvasElement: this.modelCanvas.nativeElement, // WebGL canvas canvasElement: this.modelCanvas.nativeElement as HTMLElement,
explorerElement: jQuery('.op-ifc-viewer--tree-panel')[0], // Left panel explorerElement: document.getElementsByClassName('op-ifc-viewer--tree-panel')[0] as HTMLElement, // Left panel
toolbarElement: element.find('[data-qa-selector="op-ifc-viewer--toolbar-container"]')[0], // Toolbar toolbarElement: this.toolbarElement.nativeElement as HTMLElement,
inspectorElement: element.find('[data-qa-selector="op-ifc-viewer--inspector-container"]')[0], // Toolbar inspectorElement: this.inspectorElement.nativeElement as HTMLElement,
navCubeCanvasElement: element.find('[data-qa-selector="op-ifc-viewer--nav-cube-canvas"]')[0], navCubeCanvasElement: this.navCubeCanvas.nativeElement as HTMLElement,
busyModelBackdropElement: element.find('.xeokit-busy-modal-backdrop')[0], busyModelBackdropElement: this.viewerContainer.nativeElement as HTMLElement,
keyboardEventsElement: this.modelCanvas.nativeElement as HTMLElement,
enableEditModels: manageIfcModelsAllowed, enableEditModels: manageIfcModelsAllowed,
keyboardEventsElement: this.modelCanvas.nativeElement, // WebGL canvas
}, },
this.ifcData.projects, this.ifcData.projects,
); );
}); });
}
ngAfterViewInit():void {
this.viewInitialized$.next();
} }
ngOnDestroy():void { ngOnDestroy():void {
@ -127,12 +141,12 @@ export class IFCViewerComponent implements OnInit, OnDestroy {
@HostListener('keydown', ['$event']) @HostListener('keydown', ['$event'])
@HostListener('keyup', ['$event']) @HostListener('keyup', ['$event'])
@HostListener('keypress', ['$event']) @HostListener('keypress', ['$event'])
cancelAllKeyEvents($event:KeyboardEvent) { cancelAllKeyEvents($event:KeyboardEvent):void {
$event.stopPropagation(); $event.stopPropagation();
} }
@HostListener('mousedown') @HostListener('mousedown')
enableKeyBoard() { enableKeyBoard():void {
if (this.modelCount) { if (this.modelCount) {
this.keyboardEnabled = true; this.keyboardEnabled = true;
this.ifcViewerService.setKeyboardEnabled(true); this.ifcViewerService.setKeyboardEnabled(true);
@ -140,14 +154,14 @@ export class IFCViewerComponent implements OnInit, OnDestroy {
} }
@HostListener('window:mousedown', ['$event.target']) @HostListener('window:mousedown', ['$event.target'])
disableKeyboard(target:Element) { disableKeyboard(target:Element):void {
if (this.modelCount && !this.outerContainer.nativeElement?.contains(target)) { if (this.modelCount && !this.outerContainer.nativeElement?.contains(target)) {
this.keyboardEnabled = false; this.keyboardEnabled = false;
this.ifcViewerService.setKeyboardEnabled(false); this.ifcViewerService.setKeyboardEnabled(false);
} }
} }
enableFromIcon(event:MouseEvent) { enableFromIcon(event:MouseEvent):boolean {
this.enableKeyBoard(); this.enableKeyBoard();
// Focus on the canvas // Focus on the canvas

@ -78,7 +78,7 @@ describe 'BIM navigation spec',
model_page.page_shows_a_toolbar true model_page.page_shows_a_toolbar true
model_tree.sidebar_shows_viewer_menu true model_tree.sidebar_shows_viewer_menu true
expect(page).to have_selector('.wp-cards-container') expect(page).to have_selector('.wp-cards-container')
card_view.expect_work_package_listed work_package card_view.expect_work_package_listed(work_package)
end end
it 'can switch between the different view modes' do it 'can switch between the different view modes' do
@ -100,7 +100,7 @@ describe 'BIM navigation spec',
details_view.go_back details_view.go_back
details_view.expect_closed details_view.expect_closed
card_view.expect_work_package_listed work_package card_view.expect_work_package_listed(work_package)
# Go to viewer only # Go to viewer only
model_page.switch_view 'Viewer' model_page.switch_view 'Viewer'
@ -113,7 +113,7 @@ describe 'BIM navigation spec',
model_page.model_viewer_visible false model_page.model_viewer_visible false
expect(page).to have_selector('.wp-cards-container') expect(page).to have_selector('.wp-cards-container')
card_view.expect_work_package_listed work_package card_view.expect_work_package_listed(work_package)
# Go to single view # Go to single view
card_view.open_full_screen_by_details(work_package) card_view.open_full_screen_by_details(work_package)

@ -54,8 +54,12 @@ module Pages
end end
def model_viewer_visible(visible) def model_viewer_visible(visible)
selector = '.op-ifc-viewer--model-canvas' # Ensure the canvas is present
expect(page).to (visible ? have_selector(selector, wait: 10) : have_no_selector(selector, wait: 10)) canvas_selector = '.op-ifc-viewer--model-canvas'
expect(page).to (visible ? have_selector(canvas_selector, wait: 10) : have_no_selector(canvas_selector, wait: 10))
# Ensure Xeokit is initialized. Only then the toolbar is generated.
toolbar_selector = '.xeokit-toolbar'
expect(page).to (visible ? have_selector(toolbar_selector, wait: 10) : have_no_selector(toolbar_selector, wait: 10))
end end
def model_viewer_shows_a_toolbar(visible) def model_viewer_shows_a_toolbar(visible)

Loading…
Cancel
Save