From 29c31f414446df8de15bde79c8e5588a7bd89e5f Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Fri, 30 Oct 2020 13:19:37 +0100
Subject: [PATCH] bugfix: status panel causes nav errors (#707)

---
 src/services/state/viewerState.store.ts       |   4 +-
 src/services/state/viewerState/actions.ts     |   4 +
 src/services/state/viewerState/selectors.ts   |  13 ++
 .../statusCard/statusCard.component.spec.ts   | 160 +++++++++++++-----
 .../statusCard/statusCard.component.ts        | 105 +++++-------
 5 files changed, 180 insertions(+), 106 deletions(-)

diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts
index 63fefee54..daa6fe5e7 100644
--- a/src/services/state/viewerState.store.ts
+++ b/src/services/state/viewerState.store.ts
@@ -25,6 +25,7 @@ import {
   viewerStateNewViewer
 } from './viewerState.store.helper';
 import { cvtNehubaConfigToNavigationObj } from 'src/ui/viewerStateController/viewerState.useEffect';
+import { viewerStateChangeNavigation } from './viewerState/actions';
 
 export interface StateInterface {
   fetchedTemplates: any[]
@@ -162,6 +163,7 @@ export const getStateStore = ({ state = defaultState } = {}) => (prevState: Part
       fetchedTemplates: prevState.fetchedTemplates.concat(action.fetchedTemplate),
     }
   }
+  case viewerStateChangeNavigation.type:
   case CHANGE_NAVIGATION : {
     return {
       ...prevState,
@@ -271,7 +273,7 @@ export const UNLOAD_DEDICATED_LAYER = 'UNLOAD_DEDICATED_LAYER'
 export const NEWVIEWER = viewerStateNewViewer.type
 
 export const FETCHED_TEMPLATE = 'FETCHED_TEMPLATE'
-export const CHANGE_NAVIGATION = 'CHANGE_NAVIGATION'
+export const CHANGE_NAVIGATION = viewerStateChangeNavigation.type
 
 export const SELECT_PARCELLATION = viewerStateSelectParcellation.type
 
diff --git a/src/services/state/viewerState/actions.ts b/src/services/state/viewerState/actions.ts
index 4da844663..1b2efd6f7 100644
--- a/src/services/state/viewerState/actions.ts
+++ b/src/services/state/viewerState/actions.ts
@@ -105,3 +105,7 @@ export const viewerStateMouseOverCustomLandmarkInPerspectiveView = createAction(
   props<{ payload: { label: string } }>()
 )
 
+export const viewerStateChangeNavigation = createAction(
+  `[viewerState] changeNavigation`,
+  props<{ navigation: any }>()
+)
diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts
index 44c4c8db7..778310c1e 100644
--- a/src/services/state/viewerState/selectors.ts
+++ b/src/services/state/viewerState/selectors.ts
@@ -1,4 +1,5 @@
 import { createSelector } from "@ngrx/store"
+import { create } from "domain"
 import { viewerStateHelperStoreName } from "../viewerState.store.helper"
 
 export const viewerStateSelectedRegionsSelector = createSelector(
@@ -32,6 +33,18 @@ export const viewerStateSelectedTemplateSelector = createSelector(
   viewerState => viewerState['templateSelected']
 )
 
+/**
+ * viewerStateSelectedTemplateSelector may have it navigation mutated to allow for initiliasation of viewer at the correct navigation
+ * in some circumstances, it may be required to get the original navigation object
+ */
+export const viewerStateSelectedTemplatePureSelector = createSelector(
+  viewerStateFetchedTemplatesSelector,
+  viewerStateSelectedTemplateSelector,
+  (fetchedTemplates, selectedTemplate) => {
+    return fetchedTemplates.find(t => t['@id'] === selectedTemplate('@id'))
+  }
+)
+
 export const viewerStateSelectedParcellationSelector = createSelector(
   state => state['viewerState'],
   viewerState => viewerState['parcellationSelected']
diff --git a/src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts b/src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts
index 5931d994a..8db6d1a4e 100644
--- a/src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts
+++ b/src/ui/nehubaContainer/statusCard/statusCard.component.spec.ts
@@ -1,9 +1,9 @@
-import { async, TestBed } from "@angular/core/testing"
+import { async, ComponentFixture, TestBed } from "@angular/core/testing"
 import { CommonModule } from "@angular/common"
 import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"
 import { StatusCardComponent } from "./statusCard.component"
 import { Directive, Component } from "@angular/core"
-import { of } from "rxjs"
+import { Observable, of } from "rxjs"
 import { ShareModule } from "src/share"
 import { StateModule } from "src/state"
 import { MockStore, provideMockStore } from "@ngrx/store/testing"
@@ -13,6 +13,9 @@ import { NoopAnimationsModule } from "@angular/platform-browser/animations"
 import { FormsModule, ReactiveFormsModule } from "@angular/forms"
 import { UtilModule } from "src/util"
 import { viewerConfigSelectorUseMobileUi } from "src/services/state/viewerConfig.store.helper"
+import { viewerStateNavigationStateSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors"
+import * as util from '../util'
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions"
 
 @Directive({
   selector: '[iav-auth-authState]',
@@ -73,55 +76,126 @@ describe('> statusCard.component.ts', () => {
       expect(fixture.debugElement.nativeElement).toBeTruthy()
     })
 
-    it('> toggle can be found if showFull is set to true', () => {
-      
-      const fixture = TestBed.createComponent(StatusCardComponent)
-      fixture.detectChanges()
-      fixture.componentInstance.showFull = true
-      fixture.detectChanges()
-      
-      const slider = fixture.debugElement.query( By.directive(MatSlideToggle) )
-      expect(slider).toBeTruthy()
-    })
+    describe('> in full mode, UIs are visible', () => {
+      let fixture: ComponentFixture<StatusCardComponent>
 
-    it('> toggling voxel/real toggle also toggles statusPanelRealSpace flag', () => {
+      beforeEach(() => {
 
-      const fixture = TestBed.createComponent(StatusCardComponent)
-      fixture.detectChanges()
-      fixture.componentInstance.showFull = true
-      fixture.detectChanges()
+        fixture = TestBed.createComponent(StatusCardComponent)
+        fixture.detectChanges()
+        fixture.componentInstance.showFull = true
+        fixture.detectChanges()
+      })
+
+      it('> toggle can be found', () => {
       
-      const prevFlag = fixture.componentInstance.statusPanelRealSpace
-      const sliderEl = fixture.debugElement.query( By.directive(MatSlideToggle) )
-      const slider = sliderEl.injector.get(MatSlideToggle)
-      slider.toggle()
-      fixture.detectChanges()
-      expect(fixture.componentInstance.statusPanelRealSpace).toEqual(!prevFlag)
+        const slider = fixture.debugElement.query( By.directive(MatSlideToggle) )
+        expect(slider).toBeTruthy()
+      })
+  
+      it('> toggling voxel/real toggle also toggles statusPanelRealSpace flag', () => {
+  
+        const prevFlag = fixture.componentInstance.statusPanelRealSpace
+        const sliderEl = fixture.debugElement.query( By.directive(MatSlideToggle) )
+        const slider = sliderEl.injector.get(MatSlideToggle)
+        slider.toggle()
+        fixture.detectChanges()
+        expect(fixture.componentInstance.statusPanelRealSpace).toEqual(!prevFlag)
+      })
+  
+      describe('> textNavigationTo', () => {
+        it('> takes into account of statusPanelRealSpace panel', () => {
+          const setNavigationStateSpy = jasmine.createSpy('setNavigationState')
+          fixture.componentInstance.nehubaViewer = {
+            setNavigationState: setNavigationStateSpy
+          } as any
+  
+          fixture.componentInstance.statusPanelRealSpace = true
+          fixture.componentInstance.textNavigateTo('1, 0, 0')
+          expect(setNavigationStateSpy).toHaveBeenCalledWith({
+            position: [1e6, 0, 0],
+            positionReal: true
+          })
+  
+          fixture.componentInstance.statusPanelRealSpace = false
+          fixture.componentInstance.textNavigateTo('1, 0, 0')
+          expect(setNavigationStateSpy).toHaveBeenCalledWith({
+            position: [1, 0, 0],
+            positionReal: false
+          })
+        })
+      })
     })
 
-    describe('> textNavigationTo', () => {
-      it('> takes into account of statusPanelRealSpace panel', () => {
-        const fixture = TestBed.createComponent(StatusCardComponent)
+    describe('> resetNavigation', () => {
+      let fixture: ComponentFixture<StatusCardComponent>
+      const mockCurrNavigation = {
+        orientation: [1,0,0,0],
+        position: [100,200,300],
+        perspectiveZoom: 1e9,
+        zoom: 1e9,
+        perspectiveOrientation: [1,0,0,0]
+      }
+
+      const mockNavState = {
+        orientation: [0,0,0,1],
+        position: [10,20,30],
+        perspectiveZoom: 1e6,
+        zoom: 1e6,
+        perspectiveOrientation: [0,0,0,1]
+      }
+      const mockTemplate = { foo:'bar', nehubaConfig: { foo2: 'bar2' } }
+
+      let getNavigationStateFromConfigSpy: jasmine.Spy = jasmine.createSpy('getNavigationStateFromConfig').and.returnValue(mockNavState)
+
+      beforeEach(() => {
+        const mockStore = TestBed.inject(MockStore)
+        mockStore.overrideSelector(viewerStateSelectedTemplatePureSelector, mockTemplate)
+        mockStore.overrideSelector(viewerStateNavigationStateSelector, mockCurrNavigation)
+        
+        spyOnProperty(util, 'getNavigationStateFromConfig').and.returnValue(getNavigationStateFromConfigSpy)
+        
+        fixture = TestBed.createComponent(StatusCardComponent)
         fixture.detectChanges()
-        const setNavigationStateSpy = jasmine.createSpy('setNavigationState')
-        fixture.componentInstance.nehubaViewer = {
-          setNavigationState: setNavigationStateSpy
-        } as any
-
-        fixture.componentInstance.statusPanelRealSpace = true
-        fixture.componentInstance.textNavigateTo('1, 0, 0')
-        expect(setNavigationStateSpy).toHaveBeenCalledWith({
-          position: [1e6, 0, 0],
-          positionReal: true
-        })
+        fixture.componentInstance.showFull = true
+        fixture.detectChanges()
+      })
+
+      for (const method of ['rotation', 'position', 'zoom' ]) {
+        describe(`> method: ${method}`, () => {
+
+          it(`> resetNavigation call calls getNavigationStateFromConfig`, () => {
+            fixture.componentInstance.resetNavigation({ [method]: true,  })
+            fixture.detectChanges()
+            expect(getNavigationStateFromConfigSpy).toHaveBeenCalled()
+          })
 
-        fixture.componentInstance.statusPanelRealSpace = false
-        fixture.componentInstance.textNavigateTo('1, 0, 0')
-        expect(setNavigationStateSpy).toHaveBeenCalledWith({
-          position: [1, 0, 0],
-          positionReal: false
+          it('> resetNavigation dispatches correct action', () => {
+
+            const mockStore = TestBed.inject(MockStore)
+            const idspatchSpy = spyOn(mockStore, 'dispatch')
+            fixture.componentInstance.resetNavigation({ [method]: true,  })
+            fixture.detectChanges()
+            
+            const overrideObj = {}
+            if (method === 'rotation') overrideObj['orientation'] = mockNavState['orientation']
+            if (method === 'position') overrideObj['position'] = mockNavState['position']
+            if (method === 'zoom') overrideObj['zoom'] = mockNavState['zoom']
+            expect(idspatchSpy).toHaveBeenCalledWith(
+              viewerStateChangeNavigation({
+                navigation: {
+                  ...mockCurrNavigation,
+                  ...overrideObj,
+                  positionReal: false,
+                  animation: {},
+                }
+              })
+            )
+          })
         })
-      })
+      }
     })
+  
+    
   })
 })
diff --git a/src/ui/nehubaContainer/statusCard/statusCard.component.ts b/src/ui/nehubaContainer/statusCard/statusCard.component.ts
index b89bc87da..8f14652f7 100644
--- a/src/ui/nehubaContainer/statusCard/statusCard.component.ts
+++ b/src/ui/nehubaContainer/statusCard/statusCard.component.ts
@@ -1,15 +1,18 @@
 import { Component, Input, OnInit, OnChanges, TemplateRef, HostBinding } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { LoggingService } from "src/logging";
-import { CHANGE_NAVIGATION, IavRootStoreInterface, ViewerStateInterface } from "src/services/stateStore.service";
+import { IavRootStoreInterface } from "src/services/stateStore.service";
 import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component";
-import { Observable, Subscription, of, combineLatest, BehaviorSubject } from "rxjs";
-import { distinctUntilChanged, shareReplay, map, filter, startWith } from "rxjs/operators";
+import { Observable, Subscription, of, combineLatest } from "rxjs";
+import { map, filter, startWith } from "rxjs/operators";
 import { MatBottomSheet } from "@angular/material/bottom-sheet";
 import { MatDialog } from "@angular/material/dialog";
 import { ARIA_LABELS } from 'common/constants'
 import { PureContantService } from "src/util";
 import { FormControl } from "@angular/forms";
+import { viewerStateNavigationStateSelector, viewerStateSelectedTemplatePureSelector } from "src/services/state/viewerState/selectors";
+import { getNavigationStateFromConfig } from "../util";
+import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions";
 
 @Component({
   selector : 'ui-status-card',
@@ -25,8 +28,8 @@ export class StatusCardComponent implements OnInit, OnChanges{
   public arialabel = ARIA_LABELS.STATUS_PANEL
   public showFull = false
 
-  private selectedTemplateRoot$: Observable<any>
-  private selectedTemplateRoot: any
+  private selectedTemplatePure: any
+  private currentNavigation: any
   private subscriptions: Subscription[] = []
 
   public navVal$: Observable<string>
@@ -41,36 +44,32 @@ export class StatusCardComponent implements OnInit, OnChanges{
   public SHOW_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.SHOW_FULL_STATUS_PANEL
   public HIDE_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.HIDE_FULL_STATUS_PANEL
   constructor(
-    private store: Store<ViewerStateInterface>,
-    private log: LoggingService,
     private store$: Store<IavRootStoreInterface>,
+    private log: LoggingService,
     private bottomSheet: MatBottomSheet,
     private dialog: MatDialog,
     private pureConstantService: PureContantService
   ) {
-    const viewerState$ = this.store$.pipe(
-      select('viewerState'),
-      shareReplay(1),
-    )
-    this.selectedTemplateRoot$ = viewerState$.pipe(
-      select('fetchedTemplates'),
-      distinctUntilChanged(),
-    )
-
     this.useTouchInterface$ = this.pureConstantService.useTouchUI$
   }
 
   ngOnInit(): void {
     this.subscriptions.push(
-      this.selectedTemplateRoot$.subscribe(template => {
-        this.selectedTemplateRoot = template.find(t => t.name === this.selectedTemplateName)
+      this.statusPanelFormCtrl.valueChanges.subscribe(val => {
+        this.statusPanelRealSpace = val
       })
     )
 
     this.subscriptions.push(
-      this.statusPanelFormCtrl.valueChanges.subscribe(val => {
-        this.statusPanelRealSpace = val
-      })
+      this.store$.pipe(
+        select(viewerStateSelectedTemplatePureSelector)
+      ).subscribe(n => this.selectedTemplatePure = n)
+    )
+
+    this.subscriptions.push(
+      this.store$.pipe(
+        select(viewerStateNavigationStateSelector)
+      ).subscribe(nav => this.currentNavigation = nav)
     )
   }
 
@@ -80,7 +79,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
       this.mouseVal$ = of(`neubaViewer is undefined`)
       return
     }
-    this.navVal$ = combineLatest(
+    this.navVal$ = combineLatest([
       this.statusPanelRealSpace$,
       this.nehubaViewer.viewerPosInReal$.pipe(
         filter(v => !!v)
@@ -88,14 +87,14 @@ export class StatusCardComponent implements OnInit, OnChanges{
       this.nehubaViewer.viewerPosInVoxel$.pipe(
         filter(v => !!v)
       )
-    ).pipe(
+    ]).pipe(
       map(([realFlag, real, voxel]) => realFlag
         ? real.map(v => `${ (v / 1e6).toFixed(3) }mm`).join(', ')
         : voxel.map(v => v.toFixed(3)).join(', ') ),
       startWith(`nehubaViewer initialising`)
     )
 
-    this.mouseVal$ = combineLatest(
+    this.mouseVal$ = combineLatest([
       this.statusPanelRealSpace$,
       this.nehubaViewer.mousePosInReal$.pipe(
         filter(v => !!v)
@@ -103,7 +102,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
       this.nehubaViewer.mousePosInVoxel$.pipe(
         filter(v => !!v)
       )
-    ).pipe(
+    ]).pipe(
       map(([realFlag, real, voxel]) => realFlag
         ? real.map(v => `${ (v/1e6).toFixed(3) }mm`).join(', ')
         : voxel.map(v => v.toFixed(3)).join(', ')),
@@ -111,7 +110,7 @@ export class StatusCardComponent implements OnInit, OnChanges{
     )
   }
 
-  statusPanelFormCtrl = new FormControl(true, [])
+  public statusPanelFormCtrl = new FormControl(true, [])
   public statusPanelRealSpace = true
   public statusPanelRealSpace$ = this.statusPanelFormCtrl.valueChanges.pipe(
     startWith(true)
@@ -143,44 +142,26 @@ export class StatusCardComponent implements OnInit, OnChanges{
    * the info re: nehubaViewer can stay there, too
    */
   public resetNavigation({rotation: rotationFlag = false, position: positionFlag = false, zoom : zoomFlag = false}: {rotation?: boolean, position?: boolean, zoom?: boolean}) {
-    const initialNgState = this.selectedTemplateRoot.nehubaConfig.dataset.initialNgState // d sa dsa
-
-    const perspectiveZoom = initialNgState ? initialNgState.perspectiveZoom : undefined
-    const perspectiveOrientation = initialNgState ? initialNgState.perspectiveOrientation : undefined
-    const zoom = (zoomFlag
-      && initialNgState
-      && initialNgState.navigation
-      && initialNgState.navigation.zoomFactor)
-      || undefined
-
-    const position = (positionFlag
-      && initialNgState
-      && initialNgState.navigation
-      && initialNgState.navigation.pose
-      && initialNgState.navigation.pose.position.voxelCoordinates
-      && initialNgState.navigation.pose.position.voxelCoordinates)
-      || undefined
-
-    const orientation = rotationFlag
-      ? [0, 0, 0, 1]
-      : undefined
-
-    this.store.dispatch({
-      type : CHANGE_NAVIGATION,
-      navigation : {
-        ...{
-          perspectiveZoom,
-          perspectiveOrientation,
-          zoom,
-          position,
-          orientation,
-        },
-        ...{
+    const {
+      orientation,
+      perspectiveOrientation,
+      perspectiveZoom,
+      position,
+      zoom
+    } = getNavigationStateFromConfig(this.selectedTemplatePure.nehubaConfig)
+
+    this.store$.dispatch(
+      viewerStateChangeNavigation({
+        navigation: {
+          ...this.currentNavigation,
+          ...(rotationFlag ? { orientation: orientation } : {}),
+          ...(positionFlag ? { position: position } : {}),
+          ...(zoomFlag ? { zoom: zoom } : {}),
           positionReal : false,
           animation : {},
-        },
-      },
-    })
+        }
+      })
+    )
   }
 
   openDialog(tmpl: TemplateRef<any>, options) {
-- 
GitLab