diff --git a/common/constants.js b/common/constants.js index 284345dec61ce5f62edaa1770fbb7b799abec7d6..3c00fdf2caf66a98b4167078c6add25f030d6f46 100644 --- a/common/constants.js +++ b/common/constants.js @@ -45,6 +45,7 @@ // additional volumes TOGGLE_SHOW_LAYER_CONTROL: `Show layer control`, + ADDITIONAL_VOLUME_CONTROL: 'Additional volumes control' } exports.IDS = { diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js index 4169f0e15599004cd2d40fd785a7a8d23125b1e0..5b97da11f2556d369ae5f7c7e1a807a63b19fd38 100644 --- a/e2e/protractor.conf.js +++ b/e2e/protractor.conf.js @@ -4,16 +4,16 @@ const chromeOpts = require('./chromeOpts') const SELENIUM_ADDRESS = process.env.SELENIUM_ADDRESS -const bsTestname = process.env.BROWSERSTACK_TEST_NAME -const bsUsername = process.env.BROWSERSTACK_USERNAME -const bsAccessKey = process.env.BROWSERSTACK_ACCESS_KEY +const { + BROWSERSTACK_TEST_NAME, + BROWSERSTACK_USERNAME, + BROWSERSTACK_ACCESS_KEY, +} = process.env const directConnect = !!process.env.DIRECT_CONNECT const PROTRACTOR_SPECS = process.env.PROTRACTOR_SPECS -const localConfig = bsUsername && bsAccessKey - ? {} - : { +const localConfig = { ...(SELENIUM_ADDRESS ? { seleniumAddress: SELENIUM_ADDRESS } : { directConnect: true } @@ -45,55 +45,55 @@ let bsLocal * MIT licensed */ const bsConfig = { - 'browserstackUser': bsUsername, - 'browserstackKey': bsAccessKey, + 'browserstackUser': BROWSERSTACK_USERNAME, + 'browserstackKey': BROWSERSTACK_ACCESS_KEY, 'capabilities': { 'build': 'protractor-browserstack', - 'name': bsTestname || 'iav_e2e', + 'name': BROWSERSTACK_TEST_NAME || 'iav_e2e', "os" : "Windows", "osVersion" : "10", 'browserName': 'chrome', - // 'browserstack.local': false, + 'browserstack.local': true, "seleniumVersion" : "4.0.0-alpha-2", 'browserstack.debug': 'true' }, "browserName" : "Chrome", "browserVersion" : "83.0", - // // Code to start browserstack local before start of test - // beforeLaunch: function(){ - // console.log("Connecting local"); - // return new Promise(function(resolve, reject){ - // bsLocal = new Local(); - // bsLocal.start({'key': bsAccessKey }, function(error) { - // if (error) return reject(error); - // console.log('Connected. Now testing...'); + // Code to start browserstack local before start of test + beforeLaunch: function(){ + console.log("Connecting local"); + return new Promise(function(resolve, reject){ + bsLocal = new Local(); + bsLocal.start({'key': BROWSERSTACK_ACCESS_KEY }, function(error) { + if (error) return reject(error); + console.log('Connected. Now testing...'); - // resolve(); - // }); - // }); - // }, + resolve(); + }); + }); + }, - // // Code to stop browserstack local after end of test - // afterLaunch: function(){ - // return new Promise(function(resolve, reject){ - // if (bsLocal) bsLocal.stop(resolve) - // else resolve() - // }); - // } + // Code to stop browserstack local after end of test + afterLaunch: function(){ + return new Promise(function(resolve, reject){ + if (bsLocal) bsLocal.stop(resolve) + else resolve() + }); + } } exports.config = { specs: [ - (PROTRACTOR_SPECS && PROTRACTOR_SPECS) || './src/**/*.prod.e2e-spec.js' + PROTRACTOR_SPECS || './src/**/*.prod.e2e-spec.js' ], jasmineNodeOpts: { defaultTimeoutInterval: 1000 * 60 * 10 }, ...( - bsAccessKey && bsUsername + BROWSERSTACK_ACCESS_KEY && BROWSERSTACK_USERNAME ? bsConfig : localConfig ), diff --git a/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js b/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js index 97c8fd98255902cf3bb3ee7bacbe03c467747528..4471e837f954c5e0b0a3020b4fd626c4ec3382af 100644 --- a/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js +++ b/e2e/src/advanced/browsingForDatasets.prod.e2e-spec.js @@ -197,7 +197,7 @@ describe('> pmap dataset preview', () => { it('> on update of layer control, pmap retains', async () => { // by default, additional layer control is collapsed - await iavPage.toggleLayerControl() + // await iavPage.toggleLayerControl() // deprecated await iavPage.wait(500) await iavPage.toggleNthLayerControl(0) await iavPage.wait(5500) diff --git a/e2e/src/advanced/nonAtlasImages.prod.e2e-spec.js b/e2e/src/advanced/nonAtlasImages.prod.e2e-spec.js index 71fc460e0815675cad1393fffd808bcf82ea749b..d9e46c2364b528cbe9c5d44000861c63a766bab2 100644 --- a/e2e/src/advanced/nonAtlasImages.prod.e2e-spec.js +++ b/e2e/src/advanced/nonAtlasImages.prod.e2e-spec.js @@ -35,7 +35,7 @@ describe('> non-atlas images', () => { searchParam.set('standaloneVolumes', '["precomputed://https://object.cscs.ch/v1/AUTH_08c08f9f119744cbbf77e216988da3eb/imgsvc-46d9d64f-bdac-418e-a41b-b7f805068c64"]') await iavPage.goto(`/?${searchParam.toString()}`, { interceptHttp: true, doNotAutomate: true }) - await iavPage.wait(10000) + await iavPage.wait(30000) const interceptedCalls = await iavPage.getInterceptedHttpCalls() expect( @@ -78,31 +78,19 @@ describe('> non-atlas images', () => { searchParam.set('previewingDatasetFiles', JSON.stringify(previewingDatasetFiles)) await iavPage.goto(`/?${searchParam.toString()}`, { interceptHttp: true, doNotAutomate: true }) - await iavPage.wait(10000) + await iavPage.wait(30000) const interceptedCalls = await iavPage.getInterceptedHttpCalls() const array = [ - 'BI-FOM-HSV_R', - 'BI-FOM-HSV_G', - 'BI-FOM-HSV_B', - 'BI', - 'BI-TIM', - 'BI-MRI', - 'BI-MRS', + "PLI Fiber Orientation Red Channel", + "PLI Fiber Orientation Green Channel", + "PLI Fiber Orientation Blue Channel", + "Blockface Image", + "PLI Transmittance", + "T2w MRI", + "MRI Labels" ] - for (const item of array) { - expect( - interceptedCalls.find(({ - method, - url - }) => { - const regex = new RegExp(item) - return method === 'GET' && regex.test(url) - }) - ).toBeTruthy() - } - expect( interceptedCalls ).toContain( @@ -188,6 +176,7 @@ describe('> non-atlas images', () => { ) ) }) + }) describe('> controls for non atlas volumes', () => { @@ -230,41 +219,11 @@ describe('> non-atlas images', () => { await iavPage.goto(`/?${searchParam.toString()}`, { forceTimeout: 20000 }) await iavPage.wait(2000) - const additionalLayerCtrlIsExpanded2 = await iavPage.additionalLayerControlIsExpanded() - expect(additionalLayerCtrlIsExpanded2).toEqual(false) - - }) - - it('if additional volumes are being shown, it can be toggled', async () => { - - const searchParam = new URLSearchParams() - searchParam.set('templateSelected', 'Big Brain (Histology)') - searchParam.set('parcellationSelected', 'Grey/White matter') - - const previewingDatasetFiles = [ - { - "datasetId":"minds/core/dataset/v1.0.0/b08a7dbc-7c75-4ce7-905b-690b2b1e8957", - "filename":"Overlay of data modalities" - } - ] - searchParam.set('previewingDatasetFiles', JSON.stringify(previewingDatasetFiles)) - - await iavPage.goto(`/?${searchParam.toString()}`, { forceTimeout: 20000 }) - await iavPage.wait(2000) - - const additionalLayerCtrlIsExpanded = await iavPage.additionalLayerControlIsExpanded() - expect(additionalLayerCtrlIsExpanded).toEqual(false) - - await iavPage.toggleLayerControl() - const additionalLayerCtrlIsExpanded2 = await iavPage.additionalLayerControlIsExpanded() expect(additionalLayerCtrlIsExpanded2).toEqual(true) - await iavPage.toggleLayerControl() - - const additionalLayerCtrlIsExpanded3 = await iavPage.additionalLayerControlIsExpanded() - expect(additionalLayerCtrlIsExpanded3).toEqual(false) - }) + }) + }) diff --git a/e2e/src/util.js b/e2e/src/util.js index 7b67ea2b20255879ebc8696d85dcb1bbd5e453c3..5d9884acfc54a15f0e243a8bcf04873f4b52d826 100644 --- a/e2e/src/util.js +++ b/e2e/src/util.js @@ -645,7 +645,7 @@ class WdLayoutPage extends WdBase{ _getAdditionalLayerControl(){ return this._browser.findElement( - By.css('[aria-label="Additional volumes control"]') + By.css(`[aria-label="${ARIA_LABELS.ADDITIONAL_VOLUME_CONTROL}"]`) ) } @@ -661,20 +661,11 @@ class WdLayoutPage extends WdBase{ additionalLayerControlIsExpanded() { return this._getAdditionalLayerControl() .findElement( - By.tagName('layer-browser') + By.css('layer-browser') ) .isDisplayed() } - // will throw if additional layer control is not visible - async toggleLayerControl(){ - return this._getAdditionalLayerControl() - .findElement( - By.css('[aria-label="Toggle expansion state of additional layer browser"]') - ) - .click() - } - async toggleNthLayerControl(idx) { const els = await this._getAdditionalLayerControl() .findElements( By.css(`[aria-label="${ARIA_LABELS.TOGGLE_SHOW_LAYER_CONTROL}"]`)) diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index deff73cac89cb51f169c118d55326a0a26030c18..1f656a997099789e1bc3c06e1ea8189e4741881a 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -41,7 +41,7 @@ import { MIN_REQ_EXPLAINER } from 'src/util/constants' import { SlServiceService } from "src/spotlight/sl-service.service"; import { PureContantService } from "src/util"; import { viewerStateSetSelectedRegions, viewerStateRemoveAdditionalLayer, viewerStateHelperSelectParcellationWithId } from "src/services/state/viewerState.store.helper"; -import { viewerStateGetOverlayingAdditionalParcellations, viewerStateParcVersionSelector } from "src/services/state/viewerState/selectors"; +import { viewerStateGetOverlayingAdditionalParcellations, viewerStateParcVersionSelector, viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors"; import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState/selectors"; import { ngViewerActionClearView } from "src/services/state/ngViewerState/actions"; import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors"; @@ -108,6 +108,10 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public MIN_REQ_EXPLAINER = MIN_REQ_EXPLAINER + public isStandaloneVolumes$ = this.store.pipe( + select(viewerStateStandAloneVolumes), + map(v => v.length > 0) + ) public selectedAdditionalLayers$ = this.store.pipe( select(viewerStateGetOverlayingAdditionalParcellations), diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 737414db6911778f0b7f7a399c5b24ce0e11e456..d09b5dfa6b0a0097677655890d531a69eed68aab 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -78,7 +78,7 @@ <div ui-nehuba-container-overlay-bottom-left class="d-inline-flex pe-none w-100 align-items-end m-2 mb-4"> <!-- only load atlas layer selector and chips if viewer is loaded --> - <ng-template [ngIf]="uiNehubaContainer.viewerLoaded"> + <ng-template [ngIf]="uiNehubaContainer.viewerLoaded && !(isStandaloneVolumes$ | async)"> <!-- Viewer Selector Container--> <atlas-layer-selector diff --git a/src/atlasViewer/atlasViewer.urlUtil.spec.ts b/src/atlasViewer/atlasViewer.urlUtil.spec.ts index 2aff96c2df6af1d1a02b27b2f12d3710399a78d0..01787b3b1631a2d108b09f8d3e633bda635df160 100644 --- a/src/atlasViewer/atlasViewer.urlUtil.spec.ts +++ b/src/atlasViewer/atlasViewer.urlUtil.spec.ts @@ -2,7 +2,7 @@ import {} from 'jasmine' import { defaultRootState } from 'src/services/stateStore.service' -import { cvtSearchParamToState, PARSING_SEARCHPARAM_ERROR, cvtStateToSearchParam } from './atlasViewer.urlUtil' +import { cvtSearchParamToState, cvtStateToSearchParam } from './atlasViewer.urlUtil' const bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json') const colin = require('!json-loader!src/res/ext/colin.json') diff --git a/src/glue.spec.ts b/src/glue.spec.ts index 60fc226caabed63a8d90fdf7202cc7866ac8036a..7f0e62218fa393fbf97bdefa7f19bd2d53e9caca 100644 --- a/src/glue.spec.ts +++ b/src/glue.spec.ts @@ -1,9 +1,9 @@ -import { TestBed, tick, fakeAsync, discardPeriodicTasks, flush } from "@angular/core/testing" +import { TestBed, tick, fakeAsync, discardPeriodicTasks } from "@angular/core/testing" import { DatasetPreviewGlue, glueSelectorGetUiStatePreviewingFiles, glueActionRemoveDatasetPreview, datasetPreviewMetaReducer, glueActionAddDatasetPreview, GlueEffects } from "./glue" import { ACTION_TO_WIDGET_TOKEN, EnumActionToWidget } from "./widget" import { provideMockStore, MockStore } from "@ngrx/store/testing" import { getRandomHex } from 'common/util' -import { EnumWidgetTypes, TypeOpenedWidget, uiActionSetPreviewingDatasetFiles } from "./services/state/uiState.store.helper" +import { EnumWidgetTypes, TypeOpenedWidget, uiActionSetPreviewingDatasetFiles, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper" import { hot } from "jasmine-marbles" import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing" import { glueActionToggleDatasetPreview } from './glue' @@ -14,6 +14,9 @@ import { EnumColorMapName } from "./util/colorMaps" import { ngViewerSelectorClearView } from "./services/state/ngViewerState/selectors" import { tap, ignoreElements } from "rxjs/operators" import { merge, of } from "rxjs" +import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./ui/databrowserModule/pure" +import { viewerStateSelectedTemplateSelector } from "./services/state/viewerState/selectors" +import { generalActionError } from "./services/stateStore.helper" const mockActionOnSpyReturnVal0 = { id: getRandomHex(), @@ -909,6 +912,50 @@ describe('> glue.ts', () => { describe('> GlueEffects', () => { + /** + * related to previews + */ + const mockTemplate = { + fullId: 'bar' + } + const mockPreviewFileIds = { + datasetId: 'foo', + filename: 'bar' + } + const mockPreviewFileIds2 = { + datasetId: 'foo2', + filename: 'bar2' + } + const mockPreviewFileIds3 = { + datasetId: 'foo3', + filename: 'bar3' + } + const mockPreviewFileIds4 = { + datasetId: 'foo4', + filename: 'bar4' + } + const previewFileNoRefSpace = { + name: 'bla bla 4', + datasetId: 'foo4', + filename: 'bar4' + } + const fittingMockPreviewFile = { + name: 'bla bla2', + datasetId: 'foo2', + filename: 'bar2', + referenceSpaces: [{ + fullId: 'bar' + }] + } + const mockPreviewFile = { + name: 'bla bla', + datasetId: 'foo', + filename: 'bar', + referenceSpaces: [{ + fullId: 'hello world' + }] + } + const defaultState = { viewerState: { templateSelected: null, @@ -919,16 +966,46 @@ describe('> glue.ts', () => { previewingDatasetFiles: [] } } - beforeEach(() => { + const mockGetDatasetPreviewFromId = jasmine.createSpy('getDatasetPreviewFromId') + + beforeEach(() => { TestBed.configureTestingModule({ providers: [ GlueEffects, provideMockStore({ initialState: defaultState - }) + }), + { + provide: GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME, + useValue: mockGetDatasetPreviewFromId + } ] }) + mockGetDatasetPreviewFromId.withArgs(mockPreviewFileIds2).and.returnValue( + hot('(a|)', { + a: fittingMockPreviewFile + }) + ) + mockGetDatasetPreviewFromId.withArgs({ datasetId: 'foo', filename: 'bar' }).and.returnValue( + hot('(a|)', { + a: mockPreviewFile + }) + ) + mockGetDatasetPreviewFromId.withArgs(mockPreviewFileIds3).and.returnValue( + hot('(a|)', { + a: null + }) + ) + mockGetDatasetPreviewFromId.withArgs(mockPreviewFileIds4).and.returnValue( + hot('(a|)', { + a: previewFileNoRefSpace + }) + ) + }) + + afterEach(() => { + mockGetDatasetPreviewFromId.calls.reset() }) describe('> regionTemplateParcChange$', () => { @@ -986,5 +1063,146 @@ describe('> glue.ts', () => { ) }) }) + + + describe('> unsuitablePreviews$', () => { + + it('> calls injected getDatasetPreviewFromId', () => { + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds2]) + + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.unsuitablePreviews$).toBeObservable( + hot('') + ) + /** + * calling twice, once to check if the dataset preview can be retrieved, the other to check the referenceSpace + */ + expect(mockGetDatasetPreviewFromId).toHaveBeenCalledTimes(2) + expect(mockGetDatasetPreviewFromId).toHaveBeenCalledWith(mockPreviewFileIds2) + }) + + it('> if getDatasetPreviewFromId throws in event stream, handles gracefully', () => { + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds3]) + + const glueEffects = TestBed.inject(GlueEffects) + + expect(glueEffects.unsuitablePreviews$).toBeObservable( + hot('a', { + a: [ mockPreviewFileIds3 ] + }) + ) + }) + + describe('> filtering out dataset previews that do not satisfy reference space requirements', () => { + it('> if reference spaces does not match the selected reference template, will emit', () => { + const mockStore = TestBed.inject(MockStore) + + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.unsuitablePreviews$).toBeObservable( + hot('a', { + a: [ mockPreviewFile ] + }) + ) + }) + }) + + describe('> keeping dataset previews that satisfy reference space criteria', () => { + it('> if ref space is undefined, keep preview', () => { + + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds4]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.unsuitablePreviews$).toBeObservable( + hot('') + ) + }) + + it('> if ref space is defined, and matches, keep preview', () => { + + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds2]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.unsuitablePreviews$).toBeObservable( + hot('') + ) + }) + }) + + }) + + describe('> uiRemoveUnsuitablePreviews$', () => { + it('> emits whenever unsuitablePreviews$ emits', () => { + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.uiRemoveUnsuitablePreviews$).toBeObservable( + hot('a', { + a: generalActionError({ + message: `Dataset previews ${mockPreviewFile.name} cannot be displayed.` + }) + }) + ) + }) + }) + + describe('> filterDatasetPreviewByTemplateSelected$', () => { + + it('> remove 1 preview datasetfile depending on unsuitablepreview$', () => { + const mockStore = TestBed.inject(MockStore) + + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.filterDatasetPreviewByTemplateSelected$).toBeObservable( + hot('a', { + a: uiActionSetPreviewingDatasetFiles({ + previewingDatasetFiles: [ ] + }) + }) + ) + + }) + it('> remove 1 preview datasetfile (get preview info fail) depending on unsuitablepreview$', () => { + const mockStore = TestBed.inject(MockStore) + + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds3]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.filterDatasetPreviewByTemplateSelected$).toBeObservable( + hot('a', { + a: uiActionSetPreviewingDatasetFiles({ + previewingDatasetFiles: [ ] + }) + }) + ) + + }) + it('> remove 2 preview datasetfile depending on unsuitablepreview$', () => { + const mockStore = TestBed.inject(MockStore) + + mockStore.overrideSelector(viewerStateSelectedTemplateSelector, mockTemplate) + mockStore.overrideSelector(uiStatePreviewingDatasetFilesSelector, [mockPreviewFileIds, mockPreviewFileIds2, mockPreviewFileIds4]) + const glueEffects = TestBed.inject(GlueEffects) + expect(glueEffects.filterDatasetPreviewByTemplateSelected$).toBeObservable( + hot('a', { + a: uiActionSetPreviewingDatasetFiles({ + previewingDatasetFiles: [ mockPreviewFileIds2, mockPreviewFileIds4 ] + }) + }) + ) + + }) + + }) + }) }) diff --git a/src/glue.ts b/src/glue.ts index 4cac5d819d134b19a9fcf0a74bcb22983772a8ca..e5eedb9ff59eb2e55e21f4ea0259c96f655ab646 100644 --- a/src/glue.ts +++ b/src/glue.ts @@ -1,7 +1,7 @@ import { uiActionSetPreviewingDatasetFiles, IDatasetPreviewData, uiStateShowBottomSheet, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper" import { OnDestroy, Injectable, Optional, Inject, InjectionToken } from "@angular/core" -import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId } from "./ui/databrowserModule/pure" -import { Subscription, Observable, forkJoin, of, merge } from "rxjs" +import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId, GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "./ui/databrowserModule/pure" +import { Subscription, Observable, forkJoin, of, merge, combineLatest } from "rxjs" import { select, Store, ActionReducer, createAction, props, createSelector, Action } from "@ngrx/store" import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip, catchError, bufferTime } from "rxjs/operators" import { TypeActionToWidget, EnumActionToWidget, ACTION_TO_WIDGET_TOKEN } from "./widget" @@ -17,6 +17,7 @@ import { Effect } from "@ngrx/effects" import { viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedParcellationSelector } from "./services/state/viewerState/selectors" import { ngViewerSelectorClearView } from "./services/state/ngViewerState/selectors" import { ngViewerActionClearView } from './services/state/ngViewerState/actions' +import { generalActionError } from "./services/stateStore.helper" const PREVIEW_FILE_TYPES_NO_UI = [ EnumPreviewFileTypes.NIFTI, @@ -100,6 +101,83 @@ export class GlueEffects { })) ) + unsuitablePreviews$: Observable<any> = merge( + /** + * filter out the dataset previews, whose details cannot be fetchd from getdatasetPreviewFromId method + */ + + this.store$.pipe( + select(uiStatePreviewingDatasetFilesSelector), + switchMap(previews => + forkJoin( + previews.map(prev => this.getDatasetPreviewFromId(prev).pipe( + // filter out the null's + filter(val => !val), + mapTo(prev) + )) + ).pipe( + filter(previewFiles => previewFiles.length > 0) + ) + ) + ), + /** + * filter out the dataset previews, whose details can be fetched from getDatasetPreviewFromId method + */ + combineLatest([ + this.store$.pipe( + select(viewerStateSelectedTemplateSelector) + ), + this.store$.pipe( + select(uiStatePreviewingDatasetFilesSelector), + switchMap(previews => + forkJoin( + previews.map(prev => this.getDatasetPreviewFromId(prev).pipe( + filter(val => !!val) + )) + ).pipe( + // filter out the null's + filter(previewFiles => previewFiles.length > 0) + ) + ), + ) + ]).pipe( + map(([ templateSelected, previewFiles ]) => + previewFiles.filter(({ referenceSpaces }) => + // if referenceSpaces of the dataset preview is undefined, assume it is suitable for all reference spaces + (!referenceSpaces) + ? false + : !referenceSpaces.some(({ fullId }) => fullId === '*' || fullId === templateSelected.fullId) + ) + ), + ) + ).pipe( + filter(arr => arr.length > 0), + shareReplay(1), + ) + + @Effect() + uiRemoveUnsuitablePreviews$: Observable<any> = this.unsuitablePreviews$.pipe( + map(previews => generalActionError({ + message: `Dataset previews ${previews.map(v => v.name)} cannot be displayed.` + })) + ) + + @Effect() + filterDatasetPreviewByTemplateSelected$: Observable<any> = this.unsuitablePreviews$.pipe( + withLatestFrom( + this.store$.pipe( + select(uiStatePreviewingDatasetFilesSelector), + ) + ), + map(([ unsuitablePreviews, previewFiles ]) => uiActionSetPreviewingDatasetFiles({ + previewingDatasetFiles: previewFiles.filter( + ({ datasetId: dsId, filename: fName }) => !unsuitablePreviews.some( + ({ datasetId, filename }) => datasetId === dsId && fName === filename + ) + ) + })) + ) + @Effect() resetConnectivityMode: Observable<any> = this.store$.pipe( select(viewerStateSelectedRegionsSelector), @@ -115,9 +193,9 @@ export class GlueEffects { ) constructor( - private store$: Store<any> + private store$: Store<any>, + @Inject(GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME) private getDatasetPreviewFromId: (arg) => Observable<any|null> ){ - } } @@ -195,10 +273,10 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{ switchMap(({ prvToShow, prvToDismiss }) => { return forkJoin({ prvToShow: prvToShow.length > 0 - ? forkJoin(...prvToShow.map(val => this.getDatasetPreviewFromId(val))) + ? forkJoin(prvToShow.map(val => this.getDatasetPreviewFromId(val))) : of([]), prvToDismiss: prvToDismiss.length > 0 - ? forkJoin(...prvToDismiss.map(val => this.getDatasetPreviewFromId(val))) + ? forkJoin(prvToDismiss.map(val => this.getDatasetPreviewFromId(val))) : of([]) }) }), @@ -229,7 +307,7 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{ public onRegionSelectChangeShowPreview$ = this.selectedRegionPreview$.pipe( switchMap(arr => arr.length > 0 - ? forkJoin(...arr.map(({ kgId, kgSchema, filename }) => this.getDatasetPreviewFromId({ datasetId: kgId, datasetSchema: kgSchema, filename }))) + ? forkJoin(arr.map(({ kgId, kgSchema, filename }) => this.getDatasetPreviewFromId({ datasetId: kgId, datasetSchema: kgSchema, filename }))) : of([]) ), map(arr => arr.filter(item => !!item)), @@ -238,7 +316,7 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{ public onRegionDeselectRemovePreview$ = this.onRegionSelectChangeShowPreview$.pipe( pairwise(), - map(([oArr, nArr]) => oArr.filter(item => { + map(([oArr, nArr]) => oArr.filter((item: any) => { return !nArr .map(DatasetPreviewGlue.GetDatasetPreviewId) .includes( diff --git a/src/main.module.ts b/src/main.module.ts index 51e875ff0926cbfba5f0d487c1b93b4786a11773..f163f11d2f07ae6a10a9fef30c54adc205a5bf40 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -57,6 +57,7 @@ import 'src/theme.scss' import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, GlueEffects } from './glue'; import { viewerStateHelperReducer, viewerStateFleshOutDetail, viewerStateMetaReducers, ViewerStateHelperEffect } from './services/state/viewerState.store.helper'; import { take } from 'rxjs/operators'; +import { UiEffects } from './services/state/uiState/ui.effects'; export function debug(reducer: ActionReducer<any>): ActionReducer<any> { return function(state, action) { @@ -99,7 +100,8 @@ export const GET_STATE_SNAPSHOT_TOKEN = new InjectionToken('GET_STATE_SNAPSHOT_T UiStateUseEffect, NewTemplateUseEffect, ViewerStateHelperEffect, - GlueEffects + GlueEffects, + UiEffects, ]), StoreModule.forRoot({ pluginState, diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts index d050b1a52450afe26a31eee9e0a2f5e4ad05b5e2..94d4d765e88e835e974fb08a45410bf466b588a3 100644 --- a/src/services/state/uiState.store.ts +++ b/src/services/state/uiState.store.ts @@ -9,8 +9,8 @@ import { IavRootStoreInterface, GENERAL_ACTION_TYPES } from '../stateStore.servi import { MatBottomSheetRef, MatBottomSheet } from '@angular/material/bottom-sheet'; import { uiStateCloseSidePanel, uiStateOpenSidePanel, uiStateCollapseSidePanel, uiStateExpandSidePanel, uiActionSetPreviewingDatasetFiles, uiStateShowBottomSheet, uiActionShowSidePanelConnectivity } from './uiState.store.helper'; import { viewerStateMouseOverCustomLandmark } from './viewerState/actions'; - -export const defaultState: StateInterface = { +import { IUiState } from './uiState/common' +export const defaultState: IUiState = { previewingDatasetFiles: [], mouseOverSegments: [], @@ -32,7 +32,9 @@ export const defaultState: StateInterface = { agreedKgTos: localStorage.getItem(LOCAL_STORAGE_CONST.AGREE_KG_TOS) === KG_TOS_VERSION, } -export const getStateStore = ({ state = defaultState } = {}) => (prevState: StateInterface = state, action: ActionInterface) => { +export { IUiState } + +export const getStateStore = ({ state = defaultState } = {}) => (prevState: IUiState = state, action: ActionInterface) => { switch (action.type) { case uiActionSetPreviewingDatasetFiles.type: { @@ -144,30 +146,6 @@ export function stateStore(state, action) { return defaultStateStore(state, action) } -export interface StateInterface { - previewingDatasetFiles: {datasetId: string, filename: string}[] - - mouseOverSegments: Array<{ - layer: { - name: string - } - segment: any | null - }> - sidePanelIsOpen: boolean - sidePanelExploreCurrentViewIsOpen: boolean - mouseOverSegment: any | number - - mouseOverLandmark: any - mouseOverUserLandmark: any - - focusedSidePanel: string | null - - snackbarMessage: string - - agreedCookies: boolean - agreedKgTos: boolean -} - export interface ActionInterface extends Action { segment: any | number landmark: any diff --git a/src/services/state/uiState/common.ts b/src/services/state/uiState/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..5416ff23e75ef56a934aeb514865596b121fbe8f --- /dev/null +++ b/src/services/state/uiState/common.ts @@ -0,0 +1,23 @@ +export interface IUiState{ + previewingDatasetFiles: {datasetId: string, filename: string}[] + + mouseOverSegments: Array<{ + layer: { + name: string + } + segment: any | null + }> + sidePanelIsOpen: boolean + sidePanelExploreCurrentViewIsOpen: boolean + mouseOverSegment: any | number + + mouseOverLandmark: any + mouseOverUserLandmark: any + + focusedSidePanel: string | null + + snackbarMessage: string + + agreedCookies: boolean + agreedKgTos: boolean +} diff --git a/src/services/state/uiState/selectors.ts b/src/services/state/uiState/selectors.ts index a004f0598d0e2cfc538c952f275855134bd26264..3a8e3f913b509c03cf4f0bd8e767b9bd3c098ad3 100644 --- a/src/services/state/uiState/selectors.ts +++ b/src/services/state/uiState/selectors.ts @@ -1,8 +1,9 @@ import { createSelector } from "@ngrx/store"; +import { IUiState } from './common' export const uiStatePreviewingDatasetFilesSelector = createSelector( state => state['uiState'], - uiState => uiState['previewingDatasetFiles'] + (uiState: IUiState) => uiState['previewingDatasetFiles'] ) export const uiStateMouseOverSegmentsSelector = createSelector( diff --git a/src/services/state/uiState/ui.effects.ts b/src/services/state/uiState/ui.effects.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f9287652937e0d517e59c1ebfc939f61b56e211 --- /dev/null +++ b/src/services/state/uiState/ui.effects.ts @@ -0,0 +1,25 @@ +import { Injectable, OnDestroy } from "@angular/core"; +import { Actions, ofType } from "@ngrx/effects"; +import { Subscription } from "rxjs"; +import { generalActionError } from "src/services/stateStore.helper"; + +@Injectable({ + providedIn: 'root' +}) + +export class UiEffects implements OnDestroy{ + + private subscriptions: Subscription[] = [] + + constructor(private actions$: Actions){ + this.subscriptions.push( + this.actions$.pipe( + ofType(generalActionError.type) + ).subscribe(console.log) + ) + } + + ngOnDestroy(){ + while (this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe() + } +} diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts index 6a2a7e912f6641b28454676e374014cc50709a9b..b7a091bdcb30bd333cca026e685b46516cbbd71c 100644 --- a/src/services/state/viewerState/selectors.ts +++ b/src/services/state/viewerState/selectors.ts @@ -48,6 +48,11 @@ export const viewerStateAllRegionsFlattenedRegionSelector = createSelector( } ) +export const viewerStateStandAloneVolumes = createSelector( + state => state['viewerState'], + viewerState => viewerState['standaloneVolumes'] +) + export const viewerStateGetOverlayingAdditionalParcellations = createSelector( state => state[viewerStateHelperStoreName], state => state['viewerState'], diff --git a/src/services/stateStore.helper.ts b/src/services/stateStore.helper.ts index 34393f8cc8ead35f081435bd3721565f1a2ab588..172c58258dec163d118529afe22aeb3ef4af1ce4 100644 --- a/src/services/stateStore.helper.ts +++ b/src/services/stateStore.helper.ts @@ -1,7 +1,6 @@ import { createAction, props } from "@ngrx/store"; export const GENERAL_ACTION_TYPES = { - ERROR: 'ERROR', APPLY_STATE: 'APPLY_STATE', } @@ -9,3 +8,8 @@ export const generalApplyState = createAction( GENERAL_ACTION_TYPES.APPLY_STATE, props<{ state: any }>() ) + +export const generalActionError = createAction( + `[generalActionError]`, + props<{ message: string }>() +) diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts index 32d4f68bca8b24d53bed3ed9d7e23c155866fa65..1dcde5194a5a12f343646f0f056b4574265bcf60 100644 --- a/src/services/stateStore.service.ts +++ b/src/services/stateStore.service.ts @@ -16,7 +16,7 @@ import { import { ActionInterface as UIActionInterface, defaultState as uiDefaultState, - StateInterface as UIStateInterface, + IUiState, stateStore as uiState, } from './state/uiState.store' import { @@ -46,7 +46,7 @@ export { pluginState } export { viewerConfigState } export { NgViewerStateInterface, NgViewerActionInterface, ngViewerState } export { ViewerStateInterface, ViewerActionInterface, viewerState } -export { UIStateInterface, UIActionInterface, uiState } +export { IUiState, UIActionInterface, uiState } export { userConfigState, USER_CONFIG_ACTION_TYPES} export { CHANGE_NAVIGATION, DESELECT_LANDMARKS, FETCHED_TEMPLATE, NEWVIEWER, SELECT_LANDMARKS, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS } from './state/viewerState.store' @@ -54,7 +54,7 @@ export { IDataEntry, IParcellationRegion, FETCHED_DATAENTRIES, FETCHED_SPATIAL_D export { CLOSE_SIDE_PANEL, MOUSE_OVER_LANDMARK, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, COLLAPSE_SIDE_PANEL_CURRENT_VIEW, EXPAND_SIDE_PANEL_CURRENT_VIEW } from './state/uiState.store' export { UserConfigStateUseEffect } from './state/userConfigState.store' -export { GENERAL_ACTION_TYPES } from './stateStore.helper' +export { GENERAL_ACTION_TYPES, generalActionError } from './stateStore.helper' // TODO deprecate export function safeFilter(key: string) { @@ -190,7 +190,7 @@ export interface IavRootStoreInterface { ngViewerState: NgViewerStateInterface viewerState: ViewerStateInterface dataStore: any - uiState: UIStateInterface + uiState: IUiState userConfigState: UserConfigStateInterface } diff --git a/src/ui/databrowserModule/constants.ts b/src/ui/databrowserModule/constants.ts index 58ec6aa0808c2f532bdc1e218d295c9aa0c1887c..aaed7658f9b1a6bd79d2f56b0932c7319abafe79 100644 --- a/src/ui/databrowserModule/constants.ts +++ b/src/ui/databrowserModule/constants.ts @@ -95,4 +95,4 @@ export interface DatasetPreview { filename: string } -export const GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME: InjectionToken<({ datasetSchema, datasetId, filename }) => Observable<any>> = new InjectionToken('GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME') +export const GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME = new InjectionToken<({ datasetSchema, datasetId, filename }) => Observable<any|null>>('GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME') diff --git a/src/ui/databrowserModule/preview/shownPreviews.directive.ts b/src/ui/databrowserModule/preview/shownPreviews.directive.ts index bdebaf94796d878e4cfa2974b7b2820f3613a7f9..c90350c64a1a2a39aebe631c1896371e97d24691 100644 --- a/src/ui/databrowserModule/preview/shownPreviews.directive.ts +++ b/src/ui/databrowserModule/preview/shownPreviews.directive.ts @@ -2,9 +2,10 @@ import { Directive, Optional, Inject, Output, EventEmitter, OnDestroy } from "@a import { Store, select } from "@ngrx/store"; import { uiStatePreviewingDatasetFilesSelector } from "src/services/state/uiState/selectors"; import { EnumPreviewFileTypes } from "../pure"; -import { switchMap, map, startWith } from "rxjs/operators"; +import { switchMap, map, startWith, withLatestFrom } from "rxjs/operators"; import { forkJoin, of, Subscription } from "rxjs"; import { GET_KGDS_PREVIEW_INFO_FROM_ID_FILENAME } from "../pure"; +import { viewerStateSelectedTemplateSelector } from "src/services/state/viewerState/selectors"; @Directive({ selector: '[iav-shown-previews]', @@ -18,14 +19,18 @@ export class ShownPreviewsDirective implements OnDestroy{ @Output() emitter: EventEmitter<any[]> = new EventEmitter() + private templateSelected$ = this.store$.pipe( + select(viewerStateSelectedTemplateSelector) + ) + public iavAdditionalLayers$ = this.store$.pipe( select(uiStatePreviewingDatasetFilesSelector), switchMap(prevs => prevs.length > 0 - ? forkJoin(...prevs.map( - prev => this.getDatasetPreviewFromId + ? forkJoin( + prevs.map(prev => this.getDatasetPreviewFromId ? this.getDatasetPreviewFromId(prev) : of(null) - ) + ) ) : of([]) ), diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 36126aa35f494a9f60d5578cdfde1a3e79589592..6a80e1fb4bdb3f111ec81db33f1a601121191168 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -99,7 +99,8 @@ const { ZOOM_OUT, TOGGLE_SIDE_PANEL, EXPAND, - COLLAPSE + COLLAPSE, + ADDITIONAL_VOLUME_CONTROL } = ARIA_LABELS @Component({ @@ -150,7 +151,8 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { public ARIA_LABEL_TOGGLE_SIDE_PANEL = TOGGLE_SIDE_PANEL public ARIA_LABEL_EXPAND = EXPAND public ARIA_LABEL_COLLAPSE = COLLAPSE - + public ARIA_LABEL_ADDITIONAL_VOLUME_CONTROL = ADDITIONAL_VOLUME_CONTROL + public ID_MESH_LOADING_STATUS = MESH_LOADING_STATUS @ViewChild(NehubaViewerContainerDirective,{static: true}) diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 9be5d81f9eb6e105a7afad321f177b0b8bf905b9..bde6da23515a916d4dff2a198af77fabac6172a2 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -221,6 +221,7 @@ <div class="w-100 flex-grow-1 d-flex flex-column"> <preview-card class="d-block side-nav-cover flex-grow-1" + [attr.aria-label]="ARIA_LABEL_ADDITIONAL_VOLUME_CONTROL" [datasetId]="datasetId" [filename]="filename"> </preview-card> diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index 7c2dbe4bbb169f6aa43a9a24d267c09d73cc496e..c65df9baadcdfb7e58d3b840f85353c9b7d0aa98 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -119,15 +119,15 @@ export class NehubaViewerUnit implements OnInit, OnDestroy { : [1.5e9, 1.5e9, 1.5e9] } - public _s1$: any - public _s2$: any - public _s3$: any - public _s4$: any - public _s5$: any - public _s6$: any - public _s7$: any - public _s8$: any - public _s9$: any + public _s1$: any = null + public _s2$: any = null + public _s3$: any = null + public _s4$: any = null + public _s5$: any = null + public _s6$: any = null + public _s7$: any = null + public _s8$: any = null + public _s9$: any = null public _s$: any[] = [ this._s1$, diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts index 613bc6621a70f4cab31f8e36037e3c1d8bc4e2ba..74d8f40197f3b99d1d672888dea30c05ff03028f 100644 --- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts +++ b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts @@ -12,6 +12,7 @@ import { MOUSE_OVER_SEGMENTS, MOUSE_OVER_LANDMARK } from "src/services/state/uiS import { takeOnePipe } from "../nehubaContainer.component"; import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions"; import { viewerStateMouseOverCustomLandmarkInPerspectiveView } from "src/services/state/viewerState/actions"; +import { viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors"; const defaultNehubaConfig = { "configName": "", @@ -324,8 +325,7 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{ this.subscriptions.push( this.store$.pipe( - select('viewerState'), - select('standaloneVolumes'), + select(viewerStateStandAloneVolumes), filter(v => v && Array.isArray(v) && v.length > 0), distinctUntilChanged() ).subscribe(async volumes => { diff --git a/src/ui/viewerStateController/viewerState.useEffect.ts b/src/ui/viewerStateController/viewerState.useEffect.ts index d64b634afac2105eb2f34199e43b5b1259dae2ec..8edba98ef705137f2a8256a9725e06e5d95e20a9 100644 --- a/src/ui/viewerStateController/viewerState.useEffect.ts +++ b/src/ui/viewerStateController/viewerState.useEffect.ts @@ -2,13 +2,13 @@ import { Injectable, OnDestroy } from "@angular/core"; import { Actions, Effect, ofType } from "@ngrx/effects"; import { Action, select, Store } from "@ngrx/store"; import { Observable, Subscription, of, merge } from "rxjs"; -import {distinctUntilChanged, filter, map, shareReplay, withLatestFrom, switchMap, mapTo } from "rxjs/operators"; +import { distinctUntilChanged, filter, map, shareReplay, withLatestFrom, switchMap, mapTo } from "rxjs/operators"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; -import { CHANGE_NAVIGATION, FETCHED_TEMPLATE, GENERAL_ACTION_TYPES, IavRootStoreInterface, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS, viewerState } from "src/services/stateStore.service"; +import { CHANGE_NAVIGATION, FETCHED_TEMPLATE, IavRootStoreInterface, NEWVIEWER, SELECT_PARCELLATION, SELECT_REGIONS, generalActionError } from "src/services/stateStore.service"; import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "./viewerState.base"; -import {TemplateCoordinatesTransformation} from "src/services/templateCoordinatesTransformation.service"; +import { TemplateCoordinatesTransformation } from "src/services/templateCoordinatesTransformation.service"; import { CLEAR_STANDALONE_VOLUMES } from "src/services/state/viewerState.store"; -import { viewerStateToggleRegionSelect, viewerStateHelperSelectParcellationWithId, viewerStateSelectTemplateWithId, viewerStateNavigateToRegion, viewerStateHelperStoreName, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState.store.helper"; +import { viewerStateToggleRegionSelect, viewerStateHelperSelectParcellationWithId, viewerStateSelectTemplateWithId, viewerStateNavigateToRegion, viewerStateSelectedTemplateSelector } from "src/services/state/viewerState.store.helper"; import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerState/selectors"; import { ngViewerActionClearView } from "src/services/state/ngViewerState/actions"; @@ -124,12 +124,9 @@ export class ViewerStateControllerUseEffect implements OnDestroy { const { parcellations: availableParcellations } = templateSelected const newParcellation = availableParcellations.find(t => t['@id'] === id) if (!newParcellation) { - return { - type: GENERAL_ACTION_TYPES.ERROR, - payload: { - message: 'Selected parcellation not found.', - }, - } + return generalActionError({ + message: 'Selected parcellation not found.' + }) } return { type: SELECT_PARCELLATION, @@ -217,12 +214,9 @@ export class ViewerStateControllerUseEffect implements OnDestroy { map(({ newTemplateId, templateSelected, newParcellationId, fetchedTemplates, translatedCoordinate, navigation, parcellationSelected }) => { const newTemplateTobeSelected = fetchedTemplates.find(t => t['@id'] === newTemplateId) if (!newTemplateTobeSelected) { - return { - type: GENERAL_ACTION_TYPES.ERROR, - payload: { - message: 'Selected template not found.', - }, - } + return generalActionError({ + message: 'Selected template not found.' + }) } const selectParcellationWithTemplate = (newParcellationId && newTemplateTobeSelected['parcellations'].find(p => p['@id'] === newParcellationId)) @@ -262,22 +256,16 @@ export class ViewerStateControllerUseEffect implements OnDestroy { const { payload = {} } = action as ViewerStateAction const { region } = payload if (!region) { - return { - type: GENERAL_ACTION_TYPES.ERROR, - payload: { - message: `Go to region: region not defined`, - }, - } + return generalActionError({ + message: `Go to region: region not defined` + }) } const { position } = region if (!position) { - return { - type: GENERAL_ACTION_TYPES.ERROR, - payload: { - message: `${region.name} - does not have a position defined`, - }, - } + return generalActionError({ + message: `${region.name} - does not have a position defined` + }) } return { @@ -302,12 +290,9 @@ export class ViewerStateControllerUseEffect implements OnDestroy { * if region does not have labelIndex (not tree leaf), for now, return error */ if (!region.labelIndex) { - return { - type: GENERAL_ACTION_TYPES.ERROR, - payload: { - message: 'Currently, only regions at the lowest hierarchy can be selected.', - }, - } + return generalActionError({ + message: 'Currently, only regions at the lowest hierarchy can be selected.' + }) } /**