From 63f4b716ba3b7bb74c77cf2d3deb8801b2bbe8da Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Mon, 12 Nov 2018 14:24:33 +0100
Subject: [PATCH] feat: async loading of templates chore: prettify splashscreen

---
 .../atlasViewer.dataService.service.ts        | 47 +++++++++++--------
 .../atlasViewer.urlService.service.ts         | 10 ++--
 src/services/stateStore.service.ts            | 17 +++++--
 src/ui/banner/banner.template.html            |  2 +-
 .../nehubaViewer/nehubaViewer.template.html   | 11 ++++-
 .../splashScreen/splashScreen.component.ts    | 10 +++-
 .../splashScreen/splashScreen.style.css       |  2 +
 .../splashScreen/splashScreen.template.html   | 12 ++++-
 src/ui/ui.module.ts                           |  2 +
 src/util/pipes/filterNull.pipe.ts             | 11 +++++
 10 files changed, 93 insertions(+), 31 deletions(-)
 create mode 100644 src/util/pipes/filterNull.pipe.ts

diff --git a/src/atlasViewer/atlasViewer.dataService.service.ts b/src/atlasViewer/atlasViewer.dataService.service.ts
index fbe4268ce..6a15513be 100644
--- a/src/atlasViewer/atlasViewer.dataService.service.ts
+++ b/src/atlasViewer/atlasViewer.dataService.service.ts
@@ -1,6 +1,6 @@
 import { Injectable, OnDestroy, OnInit } from "@angular/core";
 import { Store, select } from "@ngrx/store";
-import { ViewerStateInterface, FETCHED_TEMPLATES, DataEntry, FETCHED_DATAENTRIES, safeFilter, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA } from "../services/stateStore.service";
+import { ViewerStateInterface, FETCHED_TEMPLATE, DataEntry, FETCHED_DATAENTRIES, safeFilter, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA } from "../services/stateStore.service";
 import { map, distinctUntilChanged } from "rxjs/operators";
 import { Subscription } from "rxjs";
 import { AtlasViewerConstantsServices } from "./atlasViewer.constantService.service";
@@ -28,30 +28,39 @@ export class AtlasViewerDataService implements OnDestroy{
       .then(arr => resolve( [ ... arr[0], ... arr[1] ] ))
       .catch(reject)
   })
-
-  public promiseFetchedTemplates : Promise<any[]> = Promise.all(this.constantService.templateUrls.map(url=>
-    fetch(url)
-      .then(res=>
-        res.json())
-      .then(json=>json.nehubaConfig && !json.nehubaConfigURL ? 
-        Promise.resolve(json) :
-        fetch(json.nehubaConfigURL)
-          .then(r=>r.json())
-          .then(nehubaConfig=>Promise.resolve(Object.assign({},json,{ nehubaConfig })))
-      )))
   
   constructor(
     private store : Store<ViewerStateInterface>,
     private constantService : AtlasViewerConstantsServices
   ){
-
-    this.promiseFetchedTemplates
-      .then(arrJson=>
-        this.store.dispatch({
-          type : FETCHED_TEMPLATES,
-          fetchedTemplate : arrJson
+    this.constantService.templateUrls.map(url => 
+      fetch(url)
+        .then(res => res.json())
+        .then(json => new Promise((resolve, reject) => {
+          if(json.nehubaConfig)
+            resolve(json)
+          else if(json.nehubaConfigURL)
+            fetch(json.nehubaConfigURL)
+              .then(res => res.json())
+              .then(json2 => resolve(
+                Object.assign({}, json, { nehubaConfig: json2 })
+              ))
+              .catch(reject)
+          else
+            reject('neither nehubaConfig nor nehubaConfigURL defined')
+        }))
+        .then(json => this.store.dispatch({
+          type: FETCHED_TEMPLATE,
+          fetchedTemplate: json
         }))
-      .catch(console.error)
+        .catch(e => {
+          console.warn('fetching template url failed', e)
+          this.store.dispatch({
+            type: FETCHED_TEMPLATE,
+            fetchedTemplate: null
+          })
+        })
+      )
 
     this.init()
   }
diff --git a/src/atlasViewer/atlasViewer.urlService.service.ts b/src/atlasViewer/atlasViewer.urlService.service.ts
index eea96ba09..ff925f70c 100644
--- a/src/atlasViewer/atlasViewer.urlService.service.ts
+++ b/src/atlasViewer/atlasViewer.urlService.service.ts
@@ -2,9 +2,10 @@ import { Injectable } from "@angular/core";
 import { Store, select } from "@ngrx/store";
 import { ViewerStateInterface, isDefined, NEWVIEWER, getLabelIndexMap, SELECT_REGIONS, CHANGE_NAVIGATION, LOAD_DEDICATED_LAYER, ADD_NG_LAYER, PluginInitManifestInterface } from "../services/stateStore.service";
 import { Observable,combineLatest } from "rxjs";
-import { filter, map, scan, take, distinctUntilChanged, bufferTime, debounceTime } from "rxjs/operators";
+import { filter, map, scan, distinctUntilChanged, takeWhile, takeLast } from "rxjs/operators";
 import { getActiveColorMapFragmentMain } from "../ui/nehubaContainer/nehubaContainer.component";
 import { PluginServices } from "./atlasViewer.pluginService.service";
+import { AtlasViewerConstantsServices } from "./atlasViewer.constantService.service";
 
 declare var window
 
@@ -19,7 +20,8 @@ export class AtlasViewerURLService{
 
   constructor(
     private store : Store<ViewerStateInterface>,
-    private pluginService : PluginServices
+    private pluginService : PluginServices,
+    private constantService:AtlasViewerConstantsServices
   ){
 
     this.pluginState$ = this.store.pipe(
@@ -74,7 +76,9 @@ export class AtlasViewerURLService{
       select('viewerState'),
       filter(state=>isDefined(state)&&isDefined(state.fetchedTemplates)),
       map(state=>state.fetchedTemplates),
-      take(1)
+      takeWhile(fetchedTemplates => fetchedTemplates.length < this.constantService.templateUrls.length),
+      takeLast(1),
+      map(ft => ft.filter(t => t !== null))
     ).subscribe(fetchedTemplates=>{
       const searchparams = new URLSearchParams(window.location.search)
  
diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts
index 7002cd6d0..7ea4f7250 100644
--- a/src/services/stateStore.service.ts
+++ b/src/services/stateStore.service.ts
@@ -4,7 +4,7 @@ import { UserLandmark } from '../atlasViewer/atlasViewer.apiService.service';
 
 export const NEWVIEWER = 'NEWVIEWER'
 
-export const FETCHED_TEMPLATES = 'FETCHED_TEMPLATES'
+export const FETCHED_TEMPLATE = 'FETCHED_TEMPLATE'
 export const SELECT_PARCELLATION = `SELECT_PARCELLATION`
 export const SELECT_REGIONS = `SELECT_REGIONS`
 export const DESELECT_REGIONS = `DESELECT_REGIONS`
@@ -230,7 +230,13 @@ export function uiState(state:UIStateInterface = {mouseOverSegment:null, mouseOv
   }
 }
 
-export function viewerState(state:Partial<ViewerStateInterface> = {landmarksSelected : []},action:AtlasAction){
+export function viewerState(
+  state:Partial<ViewerStateInterface> = {
+    landmarksSelected : [],
+    fetchedTemplates : []
+  },
+  action:AtlasAction
+){
   switch(action.type){
     case LOAD_DEDICATED_LAYER:
       const dedicatedView = state.dedicatedView
@@ -254,9 +260,10 @@ export function viewerState(state:Partial<ViewerStateInterface> = {landmarksSele
         navigation : {},
         dedicatedView : null
       })
-    case FETCHED_TEMPLATES : {
-      return Object.assign({},state,{
-        fetchedTemplates:action.fetchedTemplate})
+    case FETCHED_TEMPLATE : {
+      return Object.assign({}, state, {
+        fetchedTemplates: state.fetchedTemplates.concat(action.fetchedTemplate)
+      })
     }
     case CHANGE_NAVIGATION : {
       return Object.assign({},state,{navigation : action.navigation})
diff --git a/src/ui/banner/banner.template.html b/src/ui/banner/banner.template.html
index a2a60e950..3cf7350f8 100644
--- a/src/ui/banner/banner.template.html
+++ b/src/ui/banner/banner.template.html
@@ -3,7 +3,7 @@
     (itemSelected) = "selectTemplate($event)"
     [activeDisplay] = "displayActiveTemplate"
     [selectedItem] = "selectedTemplate"
-    [inputArray] = "loadedTemplates$ | async">
+    [inputArray] = "loadedTemplates$ | async | filterNull">
   
   </dropdown-component>
   
diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html
index 35a646dee..a17ab3e39 100644
--- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html
+++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.template.html
@@ -1,3 +1,12 @@
 <div id = "neuroglancer-container">
-  Loading Nehuba Viewer ...
+  <div *ngIf = "constantService.loadExportNehubaPromise ? !(constantService.loadExportNehubaPromise | async) : true">
+    <h1 tyle = "text-align:center;">
+      Loading Nehuba Viewer ...
+    </h1>
+    <h1 style = "text-align:center;">
+      <span class = "homeAnimationDots loadingAnimationDots">&bull;</span>
+      <span class = "homeAnimationDots loadingAnimationDots">&bull;</span>
+      <span class = "homeAnimationDots loadingAnimationDots">&bull;</span>
+    </h1>
+  </div>
 </div>
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts
index 23c8ba730..f1178af03 100644
--- a/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts
+++ b/src/ui/nehubaContainer/splashScreen/splashScreen.component.ts
@@ -3,6 +3,7 @@ import { Observable } from "rxjs";
 import { Store, select } from "@ngrx/store";
 import { filter,map } from 'rxjs/operators'
 import { ViewerStateInterface, NEWVIEWER } from "../../../services/stateStore.service";
+import { AtlasViewerConstantsServices } from "../../../atlasViewer/atlasViewer.constantService.service";
 
 @Component({
   selector : 'ui-splashscreen',
@@ -14,7 +15,10 @@ import { ViewerStateInterface, NEWVIEWER } from "../../../services/stateStore.se
 
 export class SplashScreen{
   loadedTemplate$ : Observable<any[]>
-  constructor(private store:Store<ViewerStateInterface>){
+  constructor(
+    private store:Store<ViewerStateInterface>,
+    private constanceService: AtlasViewerConstantsServices  
+  ){
     this.loadedTemplate$ = this.store.pipe(
       select('viewerState'),
       filter((state:ViewerStateInterface)=> typeof state !== 'undefined' && typeof state.fetchedTemplates !== 'undefined' && state.fetchedTemplates !== null),
@@ -28,4 +32,8 @@ export class SplashScreen{
       selectParcellation : template.parcellations[0]
     })
   }
+
+  get totalTemplates(){
+    return this.constanceService.templateUrls.length
+  }
 }
\ No newline at end of file
diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.style.css b/src/ui/nehubaContainer/splashScreen/splashScreen.style.css
index 74504b938..d08929f3d 100644
--- a/src/ui/nehubaContainer/splashScreen/splashScreen.style.css
+++ b/src/ui/nehubaContainer/splashScreen/splashScreen.style.css
@@ -20,6 +20,8 @@ div[templateCard]
   position:relative;
   flex: 0 1 25em;
   min-width: 20em;
+  width: 20em;
+  max-width: 20em;
   padding: 0em 1em;
   margin : 0em 1em;
   box-sizing: border-box;
diff --git a/src/ui/nehubaContainer/splashScreen/splashScreen.template.html b/src/ui/nehubaContainer/splashScreen/splashScreen.template.html
index b9612c036..5ccf9fcc7 100644
--- a/src/ui/nehubaContainer/splashScreen/splashScreen.template.html
+++ b/src/ui/nehubaContainer/splashScreen/splashScreen.template.html
@@ -4,7 +4,7 @@
     templateCardContainer>
     
     <div
-      *ngFor = "let template of loadedTemplate$ | async" 
+      *ngFor = "let template of loadedTemplate$ | async | filterNull" 
       (click) = "selectTemplate(template)"
       templateCard
       hoverable>
@@ -14,5 +14,15 @@
         <small>{{ template.properties.description }}</small>
       </readmore-component>
     </div>
+    <div *ngIf = "(loadedTemplate$ | async).length < totalTemplates">
+      <h1 style = "text-align:center;">
+        <small>
+          Loading Templates
+        </small>
+        <span class = "homeAnimationDots loadingAnimationDots">&bull;</span>
+        <span class = "homeAnimationDots loadingAnimationDots">&bull;</span>
+        <span class = "homeAnimationDots loadingAnimationDots">&bull;</span>
+      </h1>
+    </div>
   </div>
 </div>
\ No newline at end of file
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index b48c6e7de..330f79c7c 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -37,6 +37,7 @@ import { DownloadDirective } from "../util/directives/download.directive";
 import { LogoContainer } from "./logoContainer/logoContainer.component";
 import { TemplateParcellationCitationsContainer } from "./templateParcellationCitations/templateParcellationCitations.component";
 import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component";
+import { FilterNullPipe } from "../util/pipes/filterNull.pipe";
 
 
 @NgModule({
@@ -81,6 +82,7 @@ import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.com
     GetLayerNameFromDatasets,
     SortDataEntriesToRegion,
     SpatialLandmarksToDataBrowserItemPipe,
+    FilterNullPipe,
 
     /* directive */
     DownloadDirective
diff --git a/src/util/pipes/filterNull.pipe.ts b/src/util/pipes/filterNull.pipe.ts
new file mode 100644
index 000000000..31a2f0c5c
--- /dev/null
+++ b/src/util/pipes/filterNull.pipe.ts
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from "@angular/core";
+
+@Pipe({
+  name: 'filterNull'
+})
+
+export class FilterNullPipe implements PipeTransform{
+  public transform(arr:any[]){
+    return arr.filter(obj => obj !== null)
+  }
+}
\ No newline at end of file
-- 
GitLab