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">•</span> + <span class = "homeAnimationDots loadingAnimationDots">•</span> + <span class = "homeAnimationDots loadingAnimationDots">•</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">•</span> + <span class = "homeAnimationDots loadingAnimationDots">•</span> + <span class = "homeAnimationDots loadingAnimationDots">•</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