diff --git a/common/constants.js b/common/constants.js index 6e92cffca6399f0182809b2f5e488ae189fc2471..878b0f2bfdca4df5d9b3d261c5e682b1ad3079db 100644 --- a/common/constants.js +++ b/common/constants.js @@ -11,7 +11,8 @@ DATASET_FILE_PREVIEW: `Preview of dataset`, PIN_DATASET: 'Toggle pinning dataset', - // overlay specific + // overlay/layout specific + SELECT_ATLAS: 'Select a different atlas', CONTEXT_MENU: `Viewer context menu`, ZOOM_IN: 'Zoom in', ZOOM_OUT: 'Zoom out', diff --git a/src/atlasViewer/atlasViewer.history.service.spec.ts b/src/atlasViewer/atlasViewer.history.service.spec.ts index eb3bc0c8a8c2b1b8b681b41a2734b47649d937e8..3e4bfb14670d9414ed0c5fc6102672cedad37326 100644 --- a/src/atlasViewer/atlasViewer.history.service.spec.ts +++ b/src/atlasViewer/atlasViewer.history.service.spec.ts @@ -5,8 +5,8 @@ import { provideMockStore } from '@ngrx/store/testing' import { Observable, of, Subscription } from 'rxjs' import { Action, Store } from '@ngrx/store' import { defaultRootState } from '../services/stateStore.service' -import { HttpClientModule } from '@angular/common/http' import { cold } from 'jasmine-marbles' +import { HttpClientTestingModule } from '@angular/common/http/testing' const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json') @@ -16,7 +16,7 @@ describe('atlasviewer.history.service.ts', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ - HttpClientModule + HttpClientTestingModule ], providers: [ AtlasViewerHistoryUseEffect, diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 34ccefe5fd1a9a177df5d4fb05e18888e4d98703..5836c9005d93e9991105e54ee3780ae38f17a6a7 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -81,38 +81,9 @@ </mat-drawer> - <!-- tag for opening and closing side nav --> - <div class="d-flex h-100 align-items-start bg-none pe-none"> - - <button mat-flat-button - matBadgePosition="above after" - matBadgeColor="accent" - [matBadge]="!sideNavDrawer.opened && (selectedRegions$ | async)?.length ? (selectedRegions$ | async)?.length : null" - [matTooltip]="!sideNavDrawer.opened ? (selectedRegions$ | async)?.length ? ('Explore ' + (selectedRegions$ | async)?.length + ' selected regions.') : 'Explore current view' : null" - [ngClass]="{'translate-x-6-n': !sideNavDrawer.opened, 'translate-x-7-n': sideNavDrawer.opened}" - class="pe-all mt-5" - (click)="toggleSideNavMenu(sideNavDrawer.opened)" - mat-drawer-trigger> - <i [ngClass]="{'fa-chevron-left': sideNavDrawer.opened, 'fa-chevron-right': !sideNavDrawer.opened}" - class="fas translate-x-3"></i> - - </button> - - <!-- visible status card when mat drawer is closed --> - <mat-card - *ngIf="!sideNavDrawer.opened" - (click)="toggleSideNavMenu(false)" - mat-ripple - mat-drawer-status-panel - class="pe-all mt-4 muted translate-x-4-n"> - <mat-card-content> - <viewer-state-mini> - </viewer-state-mini> - </mat-card-content> - <mat-card-footer></mat-card-footer> - </mat-card> - - <!-- TODO clean up menu icon --> + <div class="d-inline-block iv-custom-comp background pl-4 pt-2 pr-4"> + <atlas-dropdown-selector class="pe-all"> + </atlas-dropdown-selector> </div> </mat-drawer-container> </div> diff --git a/src/main.module.ts b/src/main.module.ts index 3f9f4e13e491e1d7ad0f73c8b6de665b5183c8b7..b1a95e344fef4ae5141a385f67d884ac5a567ae7 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -55,6 +55,7 @@ import 'src/res/css/extra_styles.css' import 'src/res/css/version.css' import 'src/theme.scss' import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue } from './glue'; +import { viewerStateHelperReducer, viewerStateFleshOutDetail } from './services/state/viewerState.store.helper'; export function debug(reducer: ActionReducer<any>): ActionReducer<any> { return function(state, action) { @@ -100,10 +101,11 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { viewerConfigState, ngViewerState, viewerState, + viewerStateHelper: viewerStateHelperReducer, uiState, userConfigState, },{ - metaReducers: [ datasetPreviewMetaReducer ] + metaReducers: [ datasetPreviewMetaReducer, viewerStateFleshOutDetail ] }), HttpClientModule, ], diff --git a/src/res/ext/MNI152.json b/src/res/ext/MNI152.json index adb408c22922930fda623838f890d0b1e80b81d4..d7abdb45dcc6115179ff16f0d69b5285ec442aef 100644 --- a/src/res/ext/MNI152.json +++ b/src/res/ext/MNI152.json @@ -1,5 +1,6 @@ { "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "@id": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "fullId": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "type": "template", "species": "Human", @@ -8,6 +9,7 @@ "nehubaConfigURL": "nehubaConfig/MNI152NehubaConfig", "parcellations": [ { + "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "JuBrain Cytoarchitectonic Atlas", "ngId": "jubrain mni152 v18 left", @@ -8113,6 +8115,7 @@ ] }, { + "@id": "juelich/iav/atlas/v1.0.0/5", "ngId": "fibre bundle long", "auxillaryMeshIndices": [ 65535 @@ -8476,6 +8479,7 @@ ] }, { + "@id": "juelich/iav/atlas/v1.0.0/6", "ngId": "fibre bundle short", "auxillaryMeshIndices": [ 65535 @@ -9521,6 +9525,7 @@ { "name": "DiFuMo Atlas (64 dimensions)", "ngId": "DiFuMo Atlas (64 dimensions)", + "@id": "minds/core/parcellationatlas/v1.0.0/d80fbab2-ce7f-4901-a3a2-3c8ef8a3b721", "auxillaryMeshIndices": [ 65535 ], @@ -10688,6 +10693,7 @@ { "name": "DiFuMo Atlas (128 dimensions)", "ngId": "DiFuMo Atlas (128 dimensions)", + "@id": "minds/core/parcellationatlas/v1.0.0/73f41e04-b7ee-4301-a828-4b298ad05ab8", "auxillaryMeshIndices": [ 65535 ], @@ -13007,6 +13013,7 @@ { "name": "DiFuMo Atlas (256 dimensions)", "ngId": "DiFuMo Atlas (256 dimensions)", + "@id": "minds/core/parcellationatlas/v1.0.0/141d510f-0342-4f94-ace7-c97d5f160235", "auxillaryMeshIndices": [ 65535 ], @@ -17630,6 +17637,7 @@ { "name": "DiFuMo Atlas (512 dimensions)", "ngId": "DiFuMo Atlas (512 dimensions)", + "@id": "minds/core/parcellationatlas/v1.0.0/63b5794f-79a4-4464-8dc1-b32e170f3d16", "auxillaryMeshIndices": [ 65535 ], @@ -26861,6 +26869,7 @@ { "name": "DiFuMo Atlas (1024 dimensions)", "ngId": "DiFuMo Atlas (1024 dimensions)", + "@id": "minds/core/parcellationatlas/v1.0.0/12fca5c5-b02c-46ce-ab9f-f12babf4c7e1", "auxillaryMeshIndices": [ 65535 ], diff --git a/src/res/ext/allenMouse.json b/src/res/ext/allenMouse.json index 724ccc849f9e2cc509a35d3748821393f4125a23..09fd34b91f0890c16b516fb6cb8ede68c62f83fe 100644 --- a/src/res/ext/allenMouse.json +++ b/src/res/ext/allenMouse.json @@ -1,5 +1,6 @@ { "name": "Allen Mouse Common Coordinate Framework v3", + "@id": "minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9", "fullId": "minds/core/referencespace/v1.0.0/265d32a0-3d84-40a5-926f-bf89f68212b9", "type": "template", "species": "Mouse", @@ -12,6 +13,7 @@ "parcellations": [ { "ngId": "v3_2017", + "@id": "minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83", "fullId": "minds/core/parcellationatlas/v1.0.0/05655b58-3b6f-49db-b285-64b5a0276f83", "name": "Allen Mouse Common Coordinate Framework v3 2017", "ngData": null, @@ -19363,6 +19365,7 @@ } }, { + "@id": "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f", "fullId": "minds/core/parcellationatlas/v1.0.0/39a1384b-8413-4d27-af8d-22432225401f", "ngId": "atlas", "name": "Allen Mouse Common Coordinate Framework v3 2015", diff --git a/src/res/ext/atlas/atlas_multiLevelHuman.json b/src/res/ext/atlas/atlas_multiLevelHuman.json index 332cc2e63a0eb12d15c4ff8abebad912215bab77..e463edcd352076d27f570854d51da7c4ecb0a09b 100644 --- a/src/res/ext/atlas/atlas_multiLevelHuman.json +++ b/src/res/ext/atlas/atlas_multiLevelHuman.json @@ -73,6 +73,7 @@ { "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "Jülich Cytoarchitechtonic Brain Atlas (human)", + "baseLayer": true, "avilableIn": [ { "@id": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", diff --git a/src/res/ext/bigbrain.json b/src/res/ext/bigbrain.json index ac63bdc45e0859679d79410fdea91e8688c498a2..417a0aef5ce0e44791ddd6d9e9c6f40db38f105e 100644 --- a/src/res/ext/bigbrain.json +++ b/src/res/ext/bigbrain.json @@ -1,5 +1,6 @@ { "name": "Big Brain (Histology)", + "@id": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "fullId": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "type": "template", "species": "Human", @@ -14,6 +15,7 @@ "nehubaConfigURL": "nehubaConfig/bigbrainNehubaConfig", "parcellations": [ { + "@id": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "fullId": "minds/core/parcellationatlas/v1.0.0/94c1125b-b87e-45e4-901c-00daee7f2579", "name": "Cytoarchitectonic Maps", "properties": { @@ -653,6 +655,7 @@ ] }, { + "@id": "juelich/iav/atlas/v1.0.0/3", "name": "BigBrain Cortical Layers Segmentation", "ngId": "cortical layers", "properties": { @@ -743,6 +746,7 @@ ] }, { + "@id": "juelich/iav/atlas/v1.0.0/4", "name": "Grey/White matter", "type": "parcellation", "ngData": null, diff --git a/src/res/ext/colin.json b/src/res/ext/colin.json index 1ee5eedbbbb61507e033a1b60a81d5853476075d..1ea080d5fcefe131189e1bff2351e3dcb30ce3a2 100644 --- a/src/res/ext/colin.json +++ b/src/res/ext/colin.json @@ -1,5 +1,6 @@ { "name": "MNI Colin 27", + "@id": "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992", "fullId": "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992", "type": "template", "species": "Human", diff --git a/src/res/ext/waxholmRatV2_0.json b/src/res/ext/waxholmRatV2_0.json index 7447530882ed1f0f2de811ff75871f065c99e281..477d3ee0eb4e598c10ca0bde070ce0c0efd25bff 100644 --- a/src/res/ext/waxholmRatV2_0.json +++ b/src/res/ext/waxholmRatV2_0.json @@ -1,5 +1,6 @@ { "name": "Waxholm Space rat brain MRI/DTI", + "@id": "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8", "fullId": "minds/core/referencespace/v1.0.0/d5717c4a-0fa1-46e6-918c-b8003069ade8", "type": "template", "species": "Rat", @@ -3203,6 +3204,7 @@ { "ngId": "v3", "type": "parcellation", + "@id": "minds/core/parcellationatlas/v1.0.0/ebb923ba-b4d5-4b82-8088-fa9215c2e1fe", "name": "Waxholm Space rat brain atlas v3", "originDatasets": [ { @@ -4812,6 +4814,7 @@ { "ngId": "v2", "type": "parcellation", + "@id": "minds/core/parcellationatlas/v1.0.0/2449a7f0-6dd0-4b5a-8f1e-aec0db03679d", "name": "Waxholm Space rat brain atlas v2", "originDatasets": [ { @@ -6674,6 +6677,7 @@ { "ngId": "v1_01", "type": "parcellation", + "@id": "minds/core/parcellationatlas/v1.0.0/11017b35-7056-4593-baad-3934d211daba", "name": "Waxholm Space rat brain atlas v1", "originDatasets": [ { diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts index a1aea6f7f7829d7ad92c1cdf9a8d9d4cdd99ae75..47b7a21b848c8656751d4ce3f7d82677c2bef173 100644 --- a/src/services/state/uiState.store.ts +++ b/src/services/state/uiState.store.ts @@ -19,7 +19,7 @@ export const defaultState: StateInterface = { mouseOverUserLandmark: null, focusedSidePanel: null, - sidePanelIsOpen: true, + sidePanelIsOpen: false, sidePanelCurrentViewContent: 'Dataset', sidePanelExploreCurrentViewIsOpen: false, diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index 5b4c59676b5e6150d9a16c2cdceea9d2166d6c0a..d50247e58296a80259a2a8c42c9b4a188eea7428 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -1,5 +1,6 @@ // TODO merge with viewerstate.store.ts when refactor is done -import { createAction, props } from "@ngrx/store"; +import { createAction, props, createReducer, on, ActionReducer, createSelector } from "@ngrx/store"; +import { reduce } from "rxjs/operators"; export interface IRegion{ name: string @@ -25,3 +26,48 @@ export const viewerStateToggleRegionSelect = createAction( `[viewerState] toggleRegionSelect`, props<{ payload: { region: any } }>() ) + +export const viewerStateSetFetchedAtlases = createAction( + '[viewerState] setFetchedatlases', + props<{ fetchedAtlases: any[] }>() +) + +export const viewerStateSelectAtlas = createAction( + `[viewerState] selectAtlas`, + props<{ atlas: { ['@id']: string } }>() +) + +interface IViewerStateHelperStore{ + fetchedAtlases: any[] + selectedAtlasId: string + selectedParcellations: any[] +} + +const initialState: IViewerStateHelperStore = { + fetchedAtlases: [], + selectedAtlasId: null, + selectedParcellations: [] +} + +export const viewerStateHelperReducer = createReducer( + initialState, + on(viewerStateSetFetchedAtlases, (state, { fetchedAtlases }) => ({ ...state, fetchedAtlases })), + on(viewerStateSelectAtlas, (state, { atlas }) => ({ ...state, selectedAtlasId: atlas['@id'] })) +) + +export const viewerStateHelperStoreName = 'viewerStateHelper' + +export const viewerStateGetParcellationsSelected = createSelector( + state => state[viewerStateHelperStoreName], + state => state.selectedParcellations +) + +export function viewerStateFleshOutDetail(reducer: ActionReducer<any>): ActionReducer<any> { + return (state, action) => { + if (action.type === viewerStateSelectAtlas.type) { + const reconstitutedAtlas = state[viewerStateHelperStoreName].fetchedAtlases.find(a => a['@id'] === (action as any).atlas['@id']) + return reducer(state, { type: action.type, atlas: reconstitutedAtlas } as any) + } + return reducer(state, action) + } +} \ No newline at end of file diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts index c39b1603221aabec3bf8c2cf3408cc0a58342e26..adaa5381ab23b7f89bd976fccc9c9a263884fd4d 100644 --- a/src/services/state/viewerState.store.ts +++ b/src/services/state/viewerState.store.ts @@ -10,7 +10,7 @@ import { LoggingService } from 'src/logging'; import { generateLabelIndexId, IavRootStoreInterface } from '../stateStore.service'; import { GENERAL_ACTION_TYPES } from '../stateStore.service' import { MOUSEOVER_USER_LANDMARK, CLOSE_SIDE_PANEL } from './uiState.store'; -import { viewerStateSetSelectedRegions, viewerStateSetConnectivityRegion } from './viewerState.store.helper'; +import { viewerStateSetSelectedRegions, viewerStateSetConnectivityRegion, viewerStateSelectAtlas } from './viewerState.store.helper'; export interface StateInterface { fetchedTemplates: any[] @@ -71,6 +71,31 @@ export const defaultState: StateInterface = { export const getStateStore = ({ state = defaultState } = {}) => (prevState: Partial<StateInterface> = state, action: ActionInterface) => { switch (action.type) { + /** + * glue code. in future, viewerStateSelectAtlas should load templates and parcellations by it self + */ + case viewerStateSelectAtlas.type: { + const { fetchedTemplates } = prevState + /** + * selecting atlas means selecting the first available templateSpace + */ + const atlas = (action as any).atlas + const templateTobeSelected = atlas.templateSpaces[0] + const templateSpaceId = templateTobeSelected['@id'] + + const parcellationId = (atlas.parcellations.find(p => !!p.baseLayer) || + templateTobeSelected.availableIn.find(p => !!p.baseLayer) || + templateTobeSelected.availableIn[0])['@id'] + + const templateSelected = fetchedTemplates.find(t => templateSpaceId === t['@id']) + const parcellationSelected = templateSelected.parcellations.find(p => p['@id'] === parcellationId) + + return { + ...prevState, + templateSelected, + parcellationSelected + } + } /** * TODO may be obsolete. test when nifti become available */ diff --git a/src/ui/atlasDropdown/atlasDropdown.component.spec.ts b/src/ui/atlasDropdown/atlasDropdown.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..70b786d12ed055a08b57f5cf47f717bf6a266301 --- /dev/null +++ b/src/ui/atlasDropdown/atlasDropdown.component.spec.ts @@ -0,0 +1 @@ +// TODO diff --git a/src/ui/atlasDropdown/atlasDropdown.component.ts b/src/ui/atlasDropdown/atlasDropdown.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8949f2aef61976866a2ea684a9c6906557d327f --- /dev/null +++ b/src/ui/atlasDropdown/atlasDropdown.component.ts @@ -0,0 +1,48 @@ +import { Component } from "@angular/core"; +import { Store, select } from "@ngrx/store"; +import { Observable } from "rxjs"; +import { distinctUntilChanged } from "rxjs/operators"; +import { viewerStateHelperStoreName, viewerStateSelectAtlas } from "src/services/state/viewerState.store.helper"; +import { ARIA_LABELS } from 'common/constants' + +@Component({ + selector: 'atlas-dropdown-selector', + templateUrl: './atlasDropdown.template.html', + styleUrls: [ + './atlasDropdown.style.css' + ] +}) + +export class AtlasDropdownSelector{ + + public fetchedAtlases$: Observable<any[]> + public selectedAtlas$: Observable<any> + + public SELECT_ATLAS_ARIA_LABEL = ARIA_LABELS.SELECT_ATLAS + + constructor(private store$: Store<any>){ + this.fetchedAtlases$ = this.store$.pipe( + select(viewerStateHelperStoreName), + select('fetchedAtlases'), + distinctUntilChanged() + ) + this.selectedAtlas$ = this.store$.pipe( + select(viewerStateHelperStoreName), + distinctUntilChanged(), + select(({ selectedAtlasId, fetchedAtlases }) => { + return fetchedAtlases.find(atlas => atlas['@id'] === selectedAtlasId) + }) + ) + } + + handleChangeAtlas({ value }) { + this.store$.dispatch( + viewerStateSelectAtlas({ + atlas: { + ['@id']: value + } + }) + ) + } +} + diff --git a/src/ui/atlasDropdown/atlasDropdown.style.css b/src/ui/atlasDropdown/atlasDropdown.style.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/ui/atlasDropdown/atlasDropdown.template.html b/src/ui/atlasDropdown/atlasDropdown.template.html new file mode 100644 index 0000000000000000000000000000000000000000..12c30c3a5d8a64f7531f77cf584e2a100a7feff2 --- /dev/null +++ b/src/ui/atlasDropdown/atlasDropdown.template.html @@ -0,0 +1,15 @@ +<mat-form-field> + <mat-label> + Atlas + </mat-label> + <mat-select + [attr.aria-label]="SELECT_ATLAS_ARIA_LABEL" + [value]="selectedAtlas$ | async | mapToProperty" + (selectionChange)="handleChangeAtlas($event)"> + <mat-option + *ngFor="let atlas of (fetchedAtlases$ | async)" + [value]="atlas['@id']"> + {{ atlas.name }} + </mat-option> + </mat-select> +</mat-form-field> diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index a112f1863088c36bff712de6cdc72ae49b7a7498..9a943cd14057504c5cebccc2530e9af4baafa466 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -90,7 +90,7 @@ import { NehubaViewerTouchDirective } from "./nehubaContainer/nehubaViewerInterf import { importNehubaFactory } from "./nehubaContainer/util"; import { APPEND_SCRIPT_TOKEN, appendScriptFactory } from "src/util/constants"; import { DOCUMENT } from "@angular/common"; - +import { AtlasDropdownSelector } from './atlasDropdown/atlasDropdown.component' @NgModule({ imports : [ @@ -126,6 +126,7 @@ import { DOCUMENT } from "@angular/common"; HelpComponent, ConfigComponent, SigninBanner, + AtlasDropdownSelector, StatusCardComponent, CookieAgreement, @@ -231,6 +232,7 @@ import { DOCUMENT } from "@angular/common"; FixedMouseContextualContainerDirective, LandmarkUIComponent, NehubaViewerTouchDirective, + AtlasDropdownSelector, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA, diff --git a/src/util/pipes/mapToProperty.pipe.spec.ts b/src/util/pipes/mapToProperty.pipe.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda --- /dev/null +++ b/src/util/pipes/mapToProperty.pipe.spec.ts @@ -0,0 +1 @@ +// TODO \ No newline at end of file diff --git a/src/util/pipes/mapToProperty.pipe.ts b/src/util/pipes/mapToProperty.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..43496f4145743984744831ea077d18b086a1c573 --- /dev/null +++ b/src/util/pipes/mapToProperty.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: 'mapToProperty', + pure: true +}) + +export class MapToPropertyPipe implements PipeTransform{ + public transform(input, property = '@id') { + return input && input[property] + } +} diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts index ff91b97cc921743853eb2d3052feaa77c9fdffd0..0e0f2a21b4abf73f4faabcd3598c32bdffdb8712 100644 --- a/src/util/pureConstant.service.ts +++ b/src/util/pureConstant.service.ts @@ -1,23 +1,60 @@ -import { Injectable } from "@angular/core"; +import { Injectable, OnDestroy } from "@angular/core"; import { Store, createSelector, select } from "@ngrx/store"; -import { Observable } from "rxjs"; +import { Observable, merge, Subscription, of } from "rxjs"; import { VIEWER_CONFIG_FEATURE_KEY, IViewerConfigState } from "src/services/state/viewerConfig.store.helper"; -import { shareReplay } from "rxjs/operators"; +import { shareReplay, tap, switchMap, scan, catchError, filter } from "rxjs/operators"; +import { HttpClient } from "@angular/common/http"; +import { BACKENDURL } from './constants' +import { viewerStateSetFetchedAtlases } from "src/services/state/viewerState.store.helper"; @Injectable({ providedIn: 'root' }) -export class PureContantService{ +export class PureContantService implements OnDestroy{ + public useTouchUI$: Observable<boolean> + public fetchedAtlases$: Observable<any> + + public totalAtlasesLength: number + private useTouchUiSelector = createSelector( state => state[VIEWER_CONFIG_FEATURE_KEY], (state: IViewerConfigState) => state.useMobileUI ) - constructor(private store: Store<any>){ + + constructor( + private store: Store<any>, + private http: HttpClient + ){ this.useTouchUI$ = this.store.pipe( select(this.useTouchUiSelector), shareReplay(1) ) + + this.fetchedAtlases$ = this.http.get(`${BACKENDURL.replace(/\/$/, '')}/atlases/`, { responseType: 'json' }).pipe( + catchError((err, obs) => of(null)), + filter(v => !!v), + tap((arr: any[]) => this.totalAtlasesLength = arr.length), + switchMap(atlases => merge( + ...atlases.map(({ url }) => this.http.get(`${BACKENDURL.replace(/\/$/, '')}/${url}`, { responseType: 'json' })) + )), + scan((acc, curr) => acc.concat(curr), []), + shareReplay(1) + ) + + this.subscriptions.push( + this.fetchedAtlases$.subscribe(fetchedAtlases => + this.store.dispatch( + viewerStateSetFetchedAtlases({ fetchedAtlases }) + ) + ) + ) + } + + private subscriptions: Subscription[] = [] + + ngOnDestroy(){ + while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() } } diff --git a/src/util/util.module.ts b/src/util/util.module.ts index f60c57f7efeaba1c4812663f0d0ee62320f50c9e..3649ee9aac8708c8201e2e10a3a996f173c4f2c5 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -13,6 +13,7 @@ import { NmToMm } from "./pipes/numbers.pipe"; import { SwitchDirective } from "./directives/switch.directive"; import { MediaQueryDirective } from './directives/mediaQuery.directive' import { LayoutModule } from "@angular/cdk/layout"; +import { MapToPropertyPipe } from "./pipes/mapToProperty.pipe"; @NgModule({ imports:[ @@ -30,7 +31,8 @@ import { LayoutModule } from "@angular/cdk/layout"; AddUnitAndJoin, NmToMm, SwitchDirective, - MediaQueryDirective + MediaQueryDirective, + MapToPropertyPipe ], exports: [ FilterNullPipe, @@ -44,7 +46,8 @@ import { LayoutModule } from "@angular/cdk/layout"; AddUnitAndJoin, NmToMm, SwitchDirective, - MediaQueryDirective + MediaQueryDirective, + MapToPropertyPipe ] })