From 22e7cd79fc5f1724df085238c64a0f4d633233d3 Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Mon, 25 Mar 2019 09:27:51 +0100
Subject: [PATCH] feat: store viewer config in local storage refactor: store

---
 src/main.module.ts                            |  18 +-
 src/services/state/dataStore.store.ts         | 120 +++++
 src/services/state/ngViewerState.store.ts     |  76 +++
 .../state/spatialSearchState.store.ts         |  42 ++
 src/services/state/uiState.store.ts           |  51 ++
 src/services/state/viewerConfig.store.ts      |   8 +-
 src/services/state/viewerState.store.ts       | 125 +++++
 src/services/stateStore.service.ts            | 453 +-----------------
 src/ui/config/config.component.ts             |  33 +-
 src/ui/config/config.template.html            |   4 +
 .../dedicated/dedicated.component.ts          |   3 +
 .../nehubaContainer.component.ts              |  22 +-
 12 files changed, 494 insertions(+), 461 deletions(-)
 create mode 100644 src/services/state/dataStore.store.ts
 create mode 100644 src/services/state/ngViewerState.store.ts
 create mode 100644 src/services/state/spatialSearchState.store.ts
 create mode 100644 src/services/state/uiState.store.ts
 create mode 100644 src/services/state/viewerState.store.ts

diff --git a/src/main.module.ts b/src/main.module.ts
index 94d90d087..daf70e99d 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -3,8 +3,8 @@ import { ComponentsModule } from "./components/components.module";
 import { UIModule } from "./ui/ui.module";
 import { LayoutModule } from "./layouts/layout.module";
 import { AtlasViewer } from "./atlasViewer/atlasViewer.component";
-import { StoreModule } from "@ngrx/store";
-import { viewerState, dataStore,spatialSearchState,uiState, ngViewerState, exportedStates } from "./services/stateStore.service";
+import { StoreModule, Store, select } from "@ngrx/store";
+import { viewerState, dataStore,spatialSearchState,uiState, ngViewerState, pluginState, viewerConfigState } from "./services/stateStore.service";
 import { GetNamesPipe } from "./util/pipes/getNames.pipe";
 import { CommonModule } from "@angular/common";
 import { GetNamePipe } from "./util/pipes/getName.pipe";
@@ -33,6 +33,7 @@ import { FloatingContainerDirective } from "./util/directives/floatingContainer.
 import { PluginFactoryDirective } from "./util/directives/pluginFactory.directive";
 import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive";
 import { AuthService } from "./services/auth.service";
+import { ViewerConfiguration } from "./services/state/viewerConfig.store";
 
 @NgModule({
   imports : [
@@ -45,8 +46,8 @@ import { AuthService } from "./services/auth.service";
     ModalModule.forRoot(),
     TooltipModule.forRoot(),
     StoreModule.forRoot({
-      pluginState: exportedStates.pluginState,
-      viewerConfigState: exportedStates.viewerConfigState,
+      pluginState,
+      viewerConfigState,
       ngViewerState,
       viewerState,
       dataStore,
@@ -105,8 +106,15 @@ import { AuthService } from "./services/auth.service";
 export class MainModule{
   
   constructor(
-    authServce: AuthService
+    authServce: AuthService,
+    store: Store<ViewerConfiguration>
   ){
     authServce.authReloadState()
+    store.pipe(
+      select('viewerConfigState')
+    ).subscribe(({ gpuLimit }) => {
+      if (gpuLimit)
+        window.localStorage.setItem('iv-gpulimit', gpuLimit.toString())
+    })
   }
 }
\ No newline at end of file
diff --git a/src/services/state/dataStore.store.ts b/src/services/state/dataStore.store.ts
new file mode 100644
index 000000000..7242028e9
--- /dev/null
+++ b/src/services/state/dataStore.store.ts
@@ -0,0 +1,120 @@
+import { Action } from '@ngrx/store'
+
+export function dataStore(state:any,action:DatasetAction){
+  switch (action.type){
+    case FETCHED_DATAENTRIES: {
+      return Object.assign({},state,{
+        fetchedDataEntries : action.fetchedDataEntries
+      })
+    }
+    case FETCHED_SPATIAL_DATA :{
+      return Object.assign({},state,{
+        fetchedSpatialData : action.fetchedDataEntries
+      })
+    }
+    case FETCHED_METADATA : {
+      return Object.assign({},state,{
+        fetchedMetadataMap : action.fetchedMetadataMap
+      })
+    }
+    default:
+      return state
+  }
+}
+
+export interface DatasetAction extends Action{
+  fetchedDataEntries : DataEntry[]
+  fetchedSpatialData : DataEntry[]
+  fetchedMetadataMap : Map<string,Map<string,{properties:Property}>>
+}
+
+export const FETCHED_DATAENTRIES = 'FETCHED_DATAENTRIES'
+export const FETCHED_METADATA = 'FETCHED_METADATA'
+export const FETCHED_SPATIAL_DATA = `FETCHED_SPATIAL_DATA`
+
+export interface DataEntry{
+  name: string
+  description: string
+  license: string[]
+  licenseInfo: string[]
+  parcellationRegion: ParcellationRegion[]
+  formats: string[]
+  custodians: string[]
+  contributors: string[]
+  referenceSpaces: ReferenceSpace[]
+  files : File[]
+  publications: Publication[]
+  embargoStatus: string[]
+
+  /**
+   * TODO typo, should be kgReferences
+   */
+  kgReference: string[]
+}
+
+export interface ParcellationRegion {
+  name: string
+}
+
+export interface ReferenceSpace {
+  name: string
+}
+
+export interface Publication{
+  name: string
+  doi : string
+  cite : string
+}
+
+export interface Property{
+  description : string
+  publications : Publication[]
+}
+
+export interface Landmark{
+  type : string //e.g. sEEG recording site, etc
+  name : string
+  templateSpace : string // possibily inherited from LandmarkBundle (?)
+  geometry : PointLandmarkGeometry | PlaneLandmarkGeometry | OtherLandmarkGeometry
+  properties : Property
+  files : File[]
+}
+
+export interface DataStateInterface{
+  fetchedDataEntries : DataEntry[]
+
+  /**
+   * Map that maps parcellation name to a Map, which maps datasetname to Property Object
+   */
+  fetchedMetadataMap : Map<string,Map<string,{properties:Property}>>
+}
+
+export interface PointLandmarkGeometry extends LandmarkGeometry{
+  position : [number, number, number]
+}
+
+export interface PlaneLandmarkGeometry extends LandmarkGeometry{
+  // corners have to be CW or CCW (no zigzag)
+  corners : [[number, number, number],[number, number, number],[number, number, number],[number, number, number]]
+}
+
+export interface OtherLandmarkGeometry extends LandmarkGeometry{
+  vertices: [number, number, number][]
+  meshIdx: [number,number,number][]
+}
+
+interface LandmarkGeometry{
+  type : 'point' | 'plane'
+  space? : 'voxel' | 'real'
+}
+
+export interface File{
+  name: string
+  absolutePath: string
+  byteSize: number
+  contentType: string
+}
+
+export interface FileSupplementData{
+  data: any
+}
\ No newline at end of file
diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts
new file mode 100644
index 000000000..d4626cbff
--- /dev/null
+++ b/src/services/state/ngViewerState.store.ts
@@ -0,0 +1,76 @@
+import { Action } from '@ngrx/store'
+
+export interface NgViewerStateInterface{
+  layers : NgLayerInterface[]
+  forceShowSegment : boolean | null
+}
+
+export interface NgViewerAction extends Action{
+  layer : NgLayerInterface
+  forceShowSegment : boolean
+}
+
+export function ngViewerState(prevState:NgViewerStateInterface = {layers:[], forceShowSegment:null}, action:NgViewerAction):NgViewerStateInterface{
+  switch(action.type){
+    case ADD_NG_LAYER:
+      return Object.assign({}, prevState, {
+        /* this configration hides the layer if a non mixable layer already present */
+        layers : action.layer.constructor === Array 
+          ? prevState.layers.concat(action.layer)
+          : prevState.layers.concat(
+              Object.assign({}, action.layer, 
+                action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0
+                  ? {visible: false}
+                  : {}))
+        
+        /* this configuration does not the addition of multiple non mixable layers */
+        // layers : action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0
+        //   ? prevState.layers
+        //   : prevState.layers.concat(action.layer)
+
+        /* this configuration allows the addition of multiple non mixables */
+        // layers : prevState.layers.map(l => mapLayer(l, action.layer)).concat(action.layer)
+      })
+    case REMOVE_NG_LAYER:
+      return Object.assign({}, prevState, {
+        layers : prevState.layers.filter(l => l.name !== action.layer.name)
+      } as NgViewerStateInterface)
+    case SHOW_NG_LAYER:
+      return Object.assign({}, prevState, {
+        layers : prevState.layers.map(l => l.name === action.layer.name
+          ? Object.assign({}, l, {
+              visible : true
+            } as NgLayerInterface)
+          : l)
+      })
+    case HIDE_NG_LAYER:
+      return Object.assign({}, prevState, {
+        layers : prevState.layers.map(l => l.name === action.layer.name
+          ? Object.assign({}, l, {
+              visible : false
+            } as NgLayerInterface)
+          : l)
+        })
+    case FORCE_SHOW_SEGMENT:
+      return Object.assign({}, prevState, {
+        forceShowSegment : action.forceShowSegment
+      }) as NgViewerStateInterface
+    default:
+      return prevState
+  }
+}
+
+export const ADD_NG_LAYER = 'ADD_NG_LAYER'
+export const REMOVE_NG_LAYER = 'REMOVE_NG_LAYER'
+export const SHOW_NG_LAYER = 'SHOW_NG_LAYER'
+export const HIDE_NG_LAYER = 'HIDE_NG_LAYER'
+export const FORCE_SHOW_SEGMENT = `FORCE_SHOW_SEGMENT`
+
+interface NgLayerInterface{
+  name : string
+  source : string
+  mixability : string // base | mixable | nonmixable
+  visible : boolean
+  shader? : string
+  transform? : any
+}
\ No newline at end of file
diff --git a/src/services/state/spatialSearchState.store.ts b/src/services/state/spatialSearchState.store.ts
new file mode 100644
index 000000000..c3a5bd4c7
--- /dev/null
+++ b/src/services/state/spatialSearchState.store.ts
@@ -0,0 +1,42 @@
+import { Action } from '@ngrx/store'
+
+export function spatialSearchState(state:SpatialDataStateInterface = initSpatialDataState, action:SpatialDataEntries){
+  switch (action.type){
+    case SPATIAL_GOTO_PAGE:
+      return Object.assign({},state,{
+        spatialSearchPagination : action.pageNo
+      })
+    case UPDATE_SPATIAL_DATA:
+      return Object.assign({},state,{
+        spatialSearchTotalResults : action.totalResults
+      })
+    case UPDATE_SPATIAL_DATA_VISIBLE:
+      return Object.assign({},state,{
+        spatialDataVisible : action.visible
+      })
+    default :
+      return state
+  }
+}
+
+export interface SpatialDataStateInterface{
+  spatialSearchPagination : number
+  spatialSearchTotalResults : number
+  spatialDataVisible : boolean
+}
+
+const initSpatialDataState : SpatialDataStateInterface = {
+  spatialDataVisible : true,
+  spatialSearchPagination : 0, 
+  spatialSearchTotalResults : 0
+}
+
+export interface SpatialDataEntries extends Action{
+  pageNo? : number
+  totalResults? : number
+  visible? : boolean
+}
+
+export const SPATIAL_GOTO_PAGE = `SPATIAL_GOTO_PAGE`
+export const UPDATE_SPATIAL_DATA = `UPDATE_SPATIAL_DATA`
+export const UPDATE_SPATIAL_DATA_VISIBLE = `UPDATE_SPATIAL_DATA_VISIBLE `
diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts
new file mode 100644
index 000000000..ebbacfb09
--- /dev/null
+++ b/src/services/state/uiState.store.ts
@@ -0,0 +1,51 @@
+import { Action } from '@ngrx/store'
+
+export function uiState(state:UIStateInterface = {mouseOverSegment:null, mouseOverLandmark : null, focusedSidePanel:null, sidePanelOpen: false},action:UIAction){
+  switch(action.type){
+    case MOUSE_OVER_SEGMENT:
+      return Object.assign({},state,{
+        mouseOverSegment : action.segment
+      })
+    case MOUSE_OVER_LANDMARK:
+      return Object.assign({}, state, {
+        mouseOverLandmark : action.landmark
+      })
+    case TOGGLE_SIDE_PANEL:
+      return Object.assign({}, state, {
+        focusedSidePanel : typeof action.focusedSidePanel  === 'undefined' || state.focusedSidePanel === action.focusedSidePanel
+          ? null
+          : action.focusedSidePanel, 
+        sidePanelOpen : !(typeof action.focusedSidePanel  === 'undefined' || state.focusedSidePanel === action.focusedSidePanel)
+      } as Partial<UIStateInterface>)
+    case OPEN_SIDE_PANEL :
+      return Object.assign({},state,{
+        sidePanelOpen : true
+      })
+    case CLOSE_SIDE_PANEL :
+      return Object.assign({},state,{
+        sidePanelOpen : false
+      })
+    default :
+      return state
+  }
+}
+
+export interface UIStateInterface{
+  sidePanelOpen : boolean
+  mouseOverSegment : any | number
+  mouseOverLandmark : any 
+  focusedSidePanel : string | null
+}
+
+export interface UIAction extends Action{
+  segment : any | number
+  landmark : any
+  focusedSidePanel? : string
+}
+
+export const MOUSE_OVER_SEGMENT = `MOUSE_OVER_SEGMENT`
+export const MOUSE_OVER_LANDMARK = `MOUSE_OVER_LANDMARK`
+
+export const TOGGLE_SIDE_PANEL = 'TOGGLE_SIDE_PANEL'
+export const CLOSE_SIDE_PANEL = `CLOSE_SIDE_PANEL`
+export const OPEN_SIDE_PANEL = `OPEN_SIDE_PANEL`
\ No newline at end of file
diff --git a/src/services/state/viewerConfig.store.ts b/src/services/state/viewerConfig.store.ts
index 230fb9941..eb634fee9 100644
--- a/src/services/state/viewerConfig.store.ts
+++ b/src/services/state/viewerConfig.store.ts
@@ -25,7 +25,12 @@ export const ACTION_TYPES = {
   CHANGE_GPU_LIMIT: `CHANGE_GPU_LIMIT`
 }
 
-export function viewerConfigState(prevState:ViewerConfiguration = {animation: CONFIG_CONSTANTS.defaultAnimation, gpuLimit: CONFIG_CONSTANTS.defaultGpuLimit}, action:ViewerConfigurationAction) {
+const lsGpuLimit = localStorage.getItem('iv-gpulimit')
+const gpuLimit = lsGpuLimit && !isNaN(Number(lsGpuLimit))
+  ? Number(lsGpuLimit)
+  : CONFIG_CONSTANTS.defaultGpuLimit
+
+export function viewerConfigState(prevState:ViewerConfiguration = {animation: CONFIG_CONSTANTS.defaultAnimation, gpuLimit}, action:ViewerConfigurationAction) {
   switch (action.type) {
     case ACTION_TYPES.UPDATE_CONFIG:
       return {
@@ -39,7 +44,6 @@ export function viewerConfigState(prevState:ViewerConfiguration = {animation: CO
           (prevState.gpuLimit || CONFIG_CONSTANTS.defaultGpuLimit) + action.payload.delta,
           CONFIG_CONSTANTS.gpuLimitMin
         ))
-
       return {
         ...prevState,
         gpuLimit: newGpuLimit
diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts
new file mode 100644
index 000000000..ce5b4c7f1
--- /dev/null
+++ b/src/services/state/viewerState.store.ts
@@ -0,0 +1,125 @@
+import { Action } from '@ngrx/store'
+import { UserLandmark } from 'src/atlasViewer/atlasViewer.apiService.service';
+
+export interface ViewerStateInterface{
+  fetchedTemplates : any[]
+
+  templateSelected : any | null
+  parcellationSelected : any | null
+  regionsSelected : any[]
+
+  landmarksSelected : any[]
+  userLandmarks : UserLandmark[]
+
+  navigation : any | null
+  dedicatedView : string[]
+}
+
+export interface AtlasAction extends Action{
+  fetchedTemplate? : any[]
+
+  selectTemplate? : any
+  selectParcellation? : any
+  selectRegions? : any[]
+  deselectRegions? : any[]
+  dedicatedView? : string
+
+  landmarks : UserLandmark[]
+  deselectLandmarks : UserLandmark[]
+
+  navigation? : any
+}
+
+export function viewerState(
+  state:Partial<ViewerStateInterface> = {
+    landmarksSelected : [],
+    fetchedTemplates : []
+  },
+  action:AtlasAction
+){
+  switch(action.type){
+    /**
+     * TODO may be obsolete. test when nifti become available
+     */
+    case LOAD_DEDICATED_LAYER:
+      const dedicatedView = state.dedicatedView
+        ? state.dedicatedView.concat(action.dedicatedView)
+        : [action.dedicatedView]
+      return Object.assign({},state,{
+        dedicatedView 
+      })
+    case UNLOAD_DEDICATED_LAYER:
+      return Object.assign({},state,{
+        dedicatedView : state.dedicatedView
+          ? state.dedicatedView.filter(dv => dv !== action.dedicatedView)
+          : []
+      })
+    case NEWVIEWER:
+      return Object.assign({},state,{
+        templateSelected : action.selectTemplate,
+        parcellationSelected : action.selectParcellation,
+        regionsSelected : [],
+        landmarksSelected : [],
+        navigation : {},
+        dedicatedView : null
+      })
+    case FETCHED_TEMPLATE : {
+      return Object.assign({}, state, {
+        fetchedTemplates: state.fetchedTemplates.concat(action.fetchedTemplate)
+      })
+    }
+    case CHANGE_NAVIGATION : {
+      return Object.assign({},state,{navigation : action.navigation})
+    }
+    case SELECT_PARCELLATION : {
+      return Object.assign({},state,{
+        parcellationSelected : action.selectParcellation,
+        regionsSelected : []
+      })
+    }
+    case DESELECT_REGIONS : {
+      return Object.assign({}, state, {
+        regionsSelected : state.regionsSelected.filter(re => action.deselectRegions.findIndex(dRe => dRe.name === re.name) < 0)
+      })
+    }
+    case SELECT_REGIONS : {
+      return Object.assign({},state,{
+        regionsSelected : action.selectRegions.map(region=>Object.assign({},region,{
+          labelIndex : Number(region.labelIndex)
+        }))
+      })
+    }
+    case DESELECT_LANDMARKS : {
+      return Object.assign({}, state, {
+        landmarksSelected : state.landmarksSelected.filter(lm => action.deselectLandmarks.findIndex(dLm => dLm.name === lm.name) < 0)
+      })
+    }
+    case SELECT_LANDMARKS : {
+      return Object.assign({}, state, {
+        landmarksSelected : action.landmarks
+      })
+    }
+    case USER_LANDMARKS : {
+      return Object.assign({}, state, {
+        userLandmarks : action.landmarks
+      })
+    }
+    default :
+      return state
+  }
+}
+
+export const LOAD_DEDICATED_LAYER = 'LOAD_DEDICATED_LAYER'
+export const UNLOAD_DEDICATED_LAYER = 'UNLOAD_DEDICATED_LAYER'
+
+export const NEWVIEWER = 'NEWVIEWER'
+
+export const FETCHED_TEMPLATE = 'FETCHED_TEMPLATE'
+export const CHANGE_NAVIGATION = 'CHANGE_NAVIGATION'
+
+export const SELECT_PARCELLATION = `SELECT_PARCELLATION`
+export const SELECT_REGIONS = `SELECT_REGIONS`
+export const DESELECT_REGIONS = `DESELECT_REGIONS`
+export const SELECT_LANDMARKS = `SELECT_LANDMARKS`
+export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS`
+export const USER_LANDMARKS = `USER_LANDMARKS`
diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts
index c39292834..68eadf1c5 100644
--- a/src/services/stateStore.service.ts
+++ b/src/services/stateStore.service.ts
@@ -1,334 +1,12 @@
-import { Action } from '@ngrx/store'
 import { filter } from 'rxjs/operators';
-import { UserLandmark } from '../atlasViewer/atlasViewer.apiService.service';
-import { pluginState } from './state/pluginState.store'
-import { viewerConfigState } from './state/viewerConfig.store'
 
-
-export const NEWVIEWER = 'NEWVIEWER'
-
-export const FETCHED_TEMPLATE = 'FETCHED_TEMPLATE'
-export const SELECT_PARCELLATION = `SELECT_PARCELLATION`
-export const SELECT_REGIONS = `SELECT_REGIONS`
-export const DESELECT_REGIONS = `DESELECT_REGIONS`
-export const SELECT_LANDMARKS = `SELECT_LANDMARKS`
-export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS`
-export const USER_LANDMARKS = `USER_LANDMARKS`
-
-export const CHANGE_NAVIGATION = 'CHANGE_NAVIGATION'
-
-export const FETCHED_DATAENTRIES = 'FETCHED_DATAENTRIES'
-export const FETCHED_METADATA = 'FETCHED_METADATA'
-export const FETCHED_SPATIAL_DATA = `FETCHED_SPATIAL_DATA`
-
-export const LOAD_DEDICATED_LAYER = 'LOAD_DEDICATED_LAYER'
-export const UNLOAD_DEDICATED_LAYER = 'UNLOAD_DEDICATED_LAYER'
-
-export const SPATIAL_GOTO_PAGE = `SPATIAL_GOTO_PAGE`
-export const UPDATE_SPATIAL_DATA = `UPDATE_SPATIAL_DATA`
-export const UPDATE_SPATIAL_DATA_VISIBLE = `UPDATE_SPATIAL_DATA_VISIBLE `
-
-export const TOGGLE_SIDE_PANEL = 'TOGGLE_SIDE_PANEL'
-export const CLOSE_SIDE_PANEL = `CLOSE_SIDE_PANEL`
-export const OPEN_SIDE_PANEL = `OPEN_SIDE_PANEL`
-
-export const MOUSE_OVER_SEGMENT = `MOUSE_OVER_SEGMENT`
-export const MOUSE_OVER_LANDMARK = `MOUSE_OVER_LANDMARK`
-
-export const FETCHED_PLUGIN_MANIFESTS = `FETCHED_PLUGIN_MANIFESTS`
-export const LAUNCH_PLUGIN = `LAUNCH_PLUGIN`
-
-
-
-export interface ViewerStateInterface{
-  fetchedTemplates : any[]
-
-  templateSelected : any | null
-  parcellationSelected : any | null
-  regionsSelected : any[]
-
-  landmarksSelected : any[]
-  userLandmarks : UserLandmark[]
-
-  navigation : any | null
-  dedicatedView : string[]
-}
-
-export interface AtlasAction extends Action{
-  fetchedTemplate? : any[]
-
-  selectTemplate? : any
-  selectParcellation? : any
-  selectRegions? : any[]
-  deselectRegions? : any[]
-  dedicatedView? : string
-
-  landmarks : UserLandmark[]
-  deselectLandmarks : UserLandmark[]
-
-  navigation? : any
-}
-
-export interface NewViewerAction extends Action{
-  selectTemplate : any,
-  selectParcellation :any
-}
-
-export interface DatasetAction extends Action{
-  fetchedDataEntries : DataEntry[]
-  fetchedSpatialData : DataEntry[]
-  fetchedMetadataMap : Map<string,Map<string,{properties:Property}>>
-}
-
-export interface DataStateInterface{
-  fetchedDataEntries : DataEntry[]
-
-  /**
-   * Map that maps parcellation name to a Map, which maps datasetname to Property Object
-   */
-  fetchedMetadataMap : Map<string,Map<string,{properties:Property}>>
-}
-
-interface NgLayerInterface{
-  name : string
-  source : string
-  mixability : string // base | mixable | nonmixable
-  visible : boolean
-  shader? : string
-  transform? : any
-}
-
-export const ADD_NG_LAYER = 'ADD_NG_LAYER'
-export const REMOVE_NG_LAYER = 'REMOVE_NG_LAYER'
-export const SHOW_NG_LAYER = 'SHOW_NG_LAYER'
-export const HIDE_NG_LAYER = 'HIDE_NG_LAYER'
-export const FORCE_SHOW_SEGMENT = `FORCE_SHOW_SEGMENT`
-
-export interface NgViewerStateInterface{
-  layers : NgLayerInterface[]
-  forceShowSegment : boolean | null
-}
-
-export interface NgViewerAction extends Action{
-  layer : NgLayerInterface
-  forceShowSegment : boolean
-}
-
-export function ngViewerState(prevState:NgViewerStateInterface = {layers:[], forceShowSegment:null}, action:NgViewerAction):NgViewerStateInterface{
-  switch(action.type){
-    case ADD_NG_LAYER:
-      return Object.assign({}, prevState, {
-        /* this configration hides the layer if a non mixable layer already present */
-        layers : action.layer.constructor === Array 
-          ? prevState.layers.concat(action.layer)
-          : prevState.layers.concat(
-              Object.assign({}, action.layer, 
-                action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0
-                  ? {visible: false}
-                  : {}))
-        
-        /* this configuration does not the addition of multiple non mixable layers */
-        // layers : action.layer.mixability === 'nonmixable' && prevState.layers.findIndex(l => l.mixability === 'nonmixable') >= 0
-        //   ? prevState.layers
-        //   : prevState.layers.concat(action.layer)
-
-        /* this configuration allows the addition of multiple non mixables */
-        // layers : prevState.layers.map(l => mapLayer(l, action.layer)).concat(action.layer)
-      })
-    case REMOVE_NG_LAYER:
-      return Object.assign({}, prevState, {
-        layers : prevState.layers.filter(l => l.name !== action.layer.name)
-      } as NgViewerStateInterface)
-    case SHOW_NG_LAYER:
-      return Object.assign({}, prevState, {
-        layers : prevState.layers.map(l => l.name === action.layer.name
-          ? Object.assign({}, l, {
-              visible : true
-            } as NgLayerInterface)
-          : l)
-      })
-    case HIDE_NG_LAYER:
-      return Object.assign({}, prevState, {
-        layers : prevState.layers.map(l => l.name === action.layer.name
-          ? Object.assign({}, l, {
-              visible : false
-            } as NgLayerInterface)
-          : l)
-        })
-    case FORCE_SHOW_SEGMENT:
-      return Object.assign({}, prevState, {
-        forceShowSegment : action.forceShowSegment
-      }) as NgViewerStateInterface
-    default:
-      return prevState
-  }
-}
-
-export function uiState(state:UIStateInterface = {mouseOverSegment:null, mouseOverLandmark : null, focusedSidePanel:null, sidePanelOpen: false},action:UIAction){
-  switch(action.type){
-    case MOUSE_OVER_SEGMENT:
-      return Object.assign({},state,{
-        mouseOverSegment : action.segment
-      })
-    case MOUSE_OVER_LANDMARK:
-      return Object.assign({}, state, {
-        mouseOverLandmark : action.landmark
-      })
-    case TOGGLE_SIDE_PANEL:
-      return Object.assign({}, state, {
-        focusedSidePanel : typeof action.focusedSidePanel  === 'undefined' || state.focusedSidePanel === action.focusedSidePanel
-          ? null
-          : action.focusedSidePanel, 
-        sidePanelOpen : !(typeof action.focusedSidePanel  === 'undefined' || state.focusedSidePanel === action.focusedSidePanel)
-      } as Partial<UIStateInterface>)
-    case OPEN_SIDE_PANEL :
-      return Object.assign({},state,{
-        sidePanelOpen : true
-      })
-    case CLOSE_SIDE_PANEL :
-      return Object.assign({},state,{
-        sidePanelOpen : false
-      })
-    default :
-      return state
-  }
-}
-
-export function viewerState(
-  state:Partial<ViewerStateInterface> = {
-    landmarksSelected : [],
-    fetchedTemplates : []
-  },
-  action:AtlasAction
-){
-  switch(action.type){
-    case LOAD_DEDICATED_LAYER:
-      const dedicatedView = state.dedicatedView
-        ? state.dedicatedView.concat(action.dedicatedView)
-        : [action.dedicatedView]
-      return Object.assign({},state,{
-        dedicatedView 
-      })
-    case UNLOAD_DEDICATED_LAYER:
-      return Object.assign({},state,{
-        dedicatedView : state.dedicatedView
-          ? state.dedicatedView.filter(dv => dv !== action.dedicatedView)
-          : []
-      })
-    case NEWVIEWER:
-      return Object.assign({},state,{
-        templateSelected : action.selectTemplate,
-        parcellationSelected : action.selectParcellation,
-        regionsSelected : [],
-        landmarksSelected : [],
-        navigation : {},
-        dedicatedView : null
-      })
-    case FETCHED_TEMPLATE : {
-      return Object.assign({}, state, {
-        fetchedTemplates: state.fetchedTemplates.concat(action.fetchedTemplate)
-      })
-    }
-    case CHANGE_NAVIGATION : {
-      return Object.assign({},state,{navigation : action.navigation})
-    }
-    case SELECT_PARCELLATION : {
-      return Object.assign({},state,{
-        parcellationSelected : action.selectParcellation,
-        regionsSelected : []
-      })
-    }
-    case DESELECT_REGIONS : {
-      return Object.assign({}, state, {
-        regionsSelected : state.regionsSelected.filter(re => action.deselectRegions.findIndex(dRe => dRe.name === re.name) < 0)
-      })
-    }
-    case SELECT_REGIONS : {
-      return Object.assign({},state,{
-        regionsSelected : action.selectRegions.map(region=>Object.assign({},region,{
-          labelIndex : Number(region.labelIndex)
-        }))
-      })
-    }
-    case DESELECT_LANDMARKS : {
-      return Object.assign({}, state, {
-        landmarksSelected : state.landmarksSelected.filter(lm => action.deselectLandmarks.findIndex(dLm => dLm.name === lm.name) < 0)
-      })
-    }
-    case SELECT_LANDMARKS : {
-      return Object.assign({}, state, {
-        landmarksSelected : action.landmarks
-      })
-    }
-    case USER_LANDMARKS : {
-      return Object.assign({}, state, {
-        userLandmarks : action.landmarks
-      })
-    }
-    default :
-      return state
-  }
-}
-
-export function dataStore(state:any,action:DatasetAction){
-  switch (action.type){
-    case FETCHED_DATAENTRIES: {
-      return Object.assign({},state,{
-        fetchedDataEntries : action.fetchedDataEntries
-      })
-    }
-    case FETCHED_SPATIAL_DATA :{
-      return Object.assign({},state,{
-        fetchedSpatialData : action.fetchedDataEntries
-      })
-    }
-    case FETCHED_METADATA : {
-      return Object.assign({},state,{
-        fetchedMetadataMap : action.fetchedMetadataMap
-      })
-    }
-    default:
-      return state
-  }
-}
-
-export interface SpatialDataEntries extends Action
-{
-  pageNo? : number
-  totalResults? : number
-  visible? : boolean
-}
-
-export interface SpatialDataStateInterface{
-  spatialSearchPagination : number
-  spatialSearchTotalResults : number
-  spatialDataVisible : boolean
-}
-
-const initSpatialDataState : SpatialDataStateInterface = {
-  spatialDataVisible : true,
-  spatialSearchPagination : 0, 
-  spatialSearchTotalResults : 0
-}
-
-export function spatialSearchState(state:SpatialDataStateInterface = initSpatialDataState, action:SpatialDataEntries){
-  switch (action.type){
-    case SPATIAL_GOTO_PAGE:
-      return Object.assign({},state,{
-        spatialSearchPagination : action.pageNo
-      })
-    case UPDATE_SPATIAL_DATA:
-      return Object.assign({},state,{
-        spatialSearchTotalResults : action.totalResults
-      })
-    case UPDATE_SPATIAL_DATA_VISIBLE:
-      return Object.assign({},state,{
-        spatialDataVisible : action.visible
-      })
-    default :
-      return state
-  }
-}
+export { viewerConfigState } from './state/viewerConfig.store'
+export { pluginState } from './state/pluginState.store'
+export { NgViewerAction, NgViewerStateInterface, ngViewerState, ADD_NG_LAYER, FORCE_SHOW_SEGMENT, HIDE_NG_LAYER, REMOVE_NG_LAYER, SHOW_NG_LAYER } from './state/ngViewerState.store'
+export { CHANGE_NAVIGATION, AtlasAction, DESELECT_LANDMARKS, DESELECT_REGIONS, FETCHED_TEMPLATE, NEWVIEWER, SELECT_LANDMARKS, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS, ViewerStateInterface, viewerState } from './state/viewerState.store'
+export { DataEntry, ParcellationRegion, DataStateInterface, DatasetAction, FETCHED_DATAENTRIES, FETCHED_METADATA, FETCHED_SPATIAL_DATA, Landmark, OtherLandmarkGeometry, PlaneLandmarkGeometry, PointLandmarkGeometry, Property, Publication, ReferenceSpace, dataStore, File, FileSupplementData } from './state/dataStore.store'
+export { CLOSE_SIDE_PANEL, MOUSE_OVER_LANDMARK, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, TOGGLE_SIDE_PANEL, UIAction, UIStateInterface, uiState } from './state/uiState.store'
+export { SPATIAL_GOTO_PAGE, SpatialDataEntries, SpatialDataStateInterface, UPDATE_SPATIAL_DATA, UPDATE_SPATIAL_DATA_VISIBLE, spatialSearchState } from './state/spatialSearchState.store'
 
 export function safeFilter(key:string){
   return filter((state:any)=>
@@ -366,123 +44,6 @@ export interface DedicatedViewState{
   dedicatedView : string | null
 }
 
-export interface DedicatedViewAction extends Action{
-  dedicatedView : string | null
-}
-
-export interface KgInfo{
-  kgId?: string
-  descrption? : string
-  /**
-   * perhaps have contributors as a separate interface?
-   */
-  contributors? : string[]
-  publications?: Publication[]
-  preparations?: string[]
-  methods?: string[]
-  license?: string
-  files? : File[]
-
-  kgUri? : string
-}
-
-export interface DataEntry{
-  name: string
-  description: string
-  license: string[]
-  licenseInfo: string[]
-  parcellationRegion: ParcellationRegion[]
-  formats: string[]
-  custodians: string[]
-  contributors: string[]
-  referenceSpaces: ReferenceSpace[]
-  files : File[]
-  publications: Publication[]
-  embargoStatus: string[]
-
-  /**
-   * TODO typo, should be kgReferences
-   */
-  kgReference: string[]
-}
-
-export interface File{
-  name: string
-  absolutePath: string
-  byteSize: number
-  contentType: string
-}
-
-export interface FileSupplementData{
-  data: any
-}
-
-export interface Property{
-  description : string
-  publications : Publication[]
-}
-
-export interface Publication{
-  name: string
-  doi : string
-  cite : string
-}
-
-export interface UIStateInterface{
-  sidePanelOpen : boolean
-  mouseOverSegment : any | number
-  mouseOverLandmark : any 
-  focusedSidePanel : string | null
-}
-
-export interface UIAction extends Action{
-  segment : any | number
-  landmark : any
-  focusedSidePanel? : string
-}
-
-export interface Landmark{
-  type : string //e.g. sEEG recording site, etc
-  name : string
-  templateSpace : string // possibily inherited from LandmarkBundle (?)
-  geometry : PointLandmarkGeometry | PlaneLandmarkGeometry | OtherLandmarkGeometry
-  properties : Property
-  files : File[]
-}
-
-export interface LandmarkGeometry{
-  type : 'point' | 'plane'
-  space? : 'voxel' | 'real'
-}
-
-export interface PointLandmarkGeometry extends LandmarkGeometry{
-  position : [number, number, number]
-}
-
-export interface PlaneLandmarkGeometry extends LandmarkGeometry{
-  // corners have to be CW or CCW (no zigzag)
-  corners : [[number, number, number],[number, number, number],[number, number, number],[number, number, number]]
-}
-
-export interface OtherLandmarkGeometry extends LandmarkGeometry{
-  vertices: [number, number, number][]
-  meshIdx: [number,number,number][]
-}
-
 export function isDefined(obj){
   return typeof obj !== 'undefined' && obj !== null
 }
-
-
-export const exportedStates = {
-  pluginState,
-  viewerConfigState
-}
-
-export interface ParcellationRegion {
-  name: string
-}
-
-export interface ReferenceSpace {
-  name: string
-}
\ No newline at end of file
diff --git a/src/ui/config/config.component.ts b/src/ui/config/config.component.ts
index 51769165b..7061bb228 100644
--- a/src/ui/config/config.component.ts
+++ b/src/ui/config/config.component.ts
@@ -1,8 +1,8 @@
-import { Component } from '@angular/core'
+import { Component, OnInit, OnDestroy } from '@angular/core'
 import { Store, select } from '@ngrx/store';
 import { ViewerConfiguration, ACTION_TYPES } from 'src/services/state/viewerConfig.store'
-import { Observable } from 'rxjs';
-import { map, distinctUntilChanged } from 'rxjs/operators';
+import { Observable, Subject, Subscription } from 'rxjs';
+import { map, distinctUntilChanged, debounce, debounceTime } from 'rxjs/operators';
 
 @Component({
   selector: 'config-component',
@@ -12,12 +12,17 @@ import { map, distinctUntilChanged } from 'rxjs/operators';
   ]
 })
 
-export class ConfigComponent{
+export class ConfigComponent implements OnInit, OnDestroy{
 
   /**
    * in MB
    */
   public gpuLimit$: Observable<number>
+  public keydown$: Subject<Event> = new Subject()
+  private subscriptions: Subscription[] = []
+
+  public gpuMin : number = 100
+  public gpuMax : number = 1000
   
   constructor(private store: Store<ViewerConfiguration>) {
     this.gpuLimit$ = this.store.pipe(
@@ -28,6 +33,26 @@ export class ConfigComponent{
     )
   }
 
+  ngOnInit(){
+    this.subscriptions.push(
+      this.keydown$.pipe(
+        debounceTime(250)
+      ).subscribe(ev => {
+        const val = (<HTMLInputElement>ev.srcElement).value
+        const numVal = val && Number(val)
+        if (isNaN(numVal) || numVal < this.gpuMin || numVal > this.gpuMax )
+          return
+        this.setGpuPreset({
+          value: numVal
+        })
+      })
+    )
+  }
+
+  ngOnDestroy(){
+    this.subscriptions.forEach(s => s.unsubscribe())
+  }
+
   public wheelEvent(ev:WheelEvent) {
     const delta = ev.deltaY * -1e5
     this.store.dispatch({
diff --git a/src/ui/config/config.template.html b/src/ui/config/config.template.html
index 34b6ef179..ab92e4430 100644
--- a/src/ui/config/config.template.html
+++ b/src/ui/config/config.template.html
@@ -5,7 +5,11 @@
   <input
     (wheel)="wheelEvent($event)"
     type="number"
+    [min]="100"
+    [max]="1000"
+    [step]="0.1"
     class="form-control"
+    (input)="keydown$.next($event)"
     [value]="gpuLimit$ | async ">
   <div class="input-group-btn">
     <div (click)="setGpuPreset({ value: 100 })" class="btn btn-default">
diff --git a/src/ui/fileviewer/dedicated/dedicated.component.ts b/src/ui/fileviewer/dedicated/dedicated.component.ts
index 41eafd3a5..94af64024 100644
--- a/src/ui/fileviewer/dedicated/dedicated.component.ts
+++ b/src/ui/fileviewer/dedicated/dedicated.component.ts
@@ -5,6 +5,9 @@ import { Observable, Subscription } from "rxjs";
 import { getActiveColorMapFragmentMain } from "../../nehubaContainer/nehubaContainer.component";
 import { ToastService } from "../../../services/toastService.service";
 
+/**
+ * TODO maybe obsolete
+ */
 
 @Component({
   selector : 'dedicated-viewer',
diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts
index 8f46c8c7e..135b23f00 100644
--- a/src/ui/nehubaContainer/nehubaContainer.component.ts
+++ b/src/ui/nehubaContainer/nehubaContainer.component.ts
@@ -3,7 +3,7 @@ import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component";
 import { Store, select } from "@ngrx/store";
 import { ViewerStateInterface, safeFilter, SELECT_REGIONS, getLabelIndexMap, CHANGE_NAVIGATION, isDefined, MOUSE_OVER_SEGMENT, USER_LANDMARKS, ADD_NG_LAYER, REMOVE_NG_LAYER, NgViewerStateInterface, MOUSE_OVER_LANDMARK, SELECT_LANDMARKS, Landmark, PointLandmarkGeometry, PlaneLandmarkGeometry, OtherLandmarkGeometry } from "../../services/stateStore.service";
 import { Observable, Subscription, fromEvent, combineLatest, merge } from "rxjs";
-import { filter,map, take, scan, debounceTime, distinctUntilChanged, switchMap, skip, withLatestFrom, buffer } from "rxjs/operators";
+import { filter,map, take, scan, debounceTime, distinctUntilChanged, switchMap, skip, withLatestFrom, buffer, tap } from "rxjs/operators";
 import { AtlasViewerAPIServices, UserLandmark } from "../../atlasViewer/atlasViewer.apiService.service";
 import { timedValues } from "../../util/generator";
 import { AtlasViewerDataService } from "../../atlasViewer/atlasViewer.dataService.service";
@@ -75,6 +75,7 @@ export class NehubaContainer implements OnInit, OnDestroy{
   private nehubaViewerSubscriptions : Subscription[] = []
 
   public nanometersToOffsetPixelsFn : Function[] = []
+  private viewerConfig : Partial<ViewerConfiguration> = {}
 
   constructor(
     private constantService : AtlasViewerConstantsServices,
@@ -90,9 +91,10 @@ export class NehubaContainer implements OnInit, OnDestroy{
        * TODO: this is only a bandaid fix. Technically, we should also implement
        * logic to take the previously set config to apply oninit
        */
-      filter(() => isDefined(this.nehubaViewer) && isDefined(this.nehubaViewer.nehubaViewer)),
       distinctUntilChanged(),
-      debounceTime(200)
+      debounceTime(200),
+      tap(viewerConfig => this.viewerConfig = viewerConfig ),
+      filter(() => isDefined(this.nehubaViewer) && isDefined(this.nehubaViewer.nehubaViewer))
     )
 
     this.nehubaViewerFactory = this.csf.resolveComponentFactory(NehubaViewerUnit)
@@ -701,7 +703,19 @@ export class NehubaContainer implements OnInit, OnDestroy{
     this.container.clear()
     this.cr = this.container.createComponent(this.nehubaViewerFactory)
     this.nehubaViewer = this.cr.instance
-    this.nehubaViewer.config = template.nehubaConfig
+
+    /**
+     * apply viewer config such as gpu limit
+     */
+    const { gpuLimit = null } = this.viewerConfig
+    const { nehubaConfig } = template
+    
+    if (gpuLimit) {
+      const initialNgState = nehubaConfig && nehubaConfig.dataset && nehubaConfig.dataset.initialNgState
+      initialNgState['gpuLimit'] = gpuLimit
+    }
+    
+    this.nehubaViewer.config = nehubaConfig
 
     /* TODO replace with id from KG */
     this.nehubaViewer.templateId = template.name
-- 
GitLab