diff --git a/common/util.js b/common/util.js index 9bf92e25986ce6b3a1e5650b550b1045a79e189b..9c66cad1bdf0964447c9e646b4035857efa24d14 100644 --- a/common/util.js +++ b/common/util.js @@ -47,6 +47,14 @@ return true } + exports.arrayOrderedEql = function arrayOrderedEql(arr1, arr2) { + if (arr1.length !== arr2.length) return false + for (const idx in arr1) { + if (arr1[idx] !== arr2[idx]) return false + } + return true + } + exports.strToRgb = str => { if (typeof str !== 'string') throw new Error(`strToRgb input must be typeof string !`) diff --git a/common/util.spec.js b/common/util.spec.js index 2af63766c202999af0a9ca2ccf57a341d947f0ee..b817b6607331d06fdb9373c20d70e1d4eb90e08a 100644 --- a/common/util.spec.js +++ b/common/util.spec.js @@ -1,4 +1,4 @@ -import { getIdFromFullId, strToRgb, verifyPositionArg } from './util' +import { getIdFromFullId, strToRgb, verifyPositionArg, arrayOrderedEql } from './util' describe('common/util.js', () => { describe('getIdFromFullId', () => { @@ -117,4 +117,37 @@ describe('common/util.js', () => { }) }) }) + + describe('> arrayOrderedEql', () => { + describe('> if array eql', () => { + it('> returns true', () => { + expect( + arrayOrderedEql(['foo', 3], ['foo', 3]) + ).toBeTrue() + }) + }) + describe('> if array not eql', () => { + describe('> not ordered eql', () => { + it('> returns false', () => { + expect( + arrayOrderedEql(['foo', 'bar'], ['bar', 'foo']) + ).toBeFalse() + }) + }) + describe('> item not eql', () => { + it('> returns false', () => { + expect( + arrayOrderedEql(['foo', null], ['foo', undefined]) + ).toBeFalse() + }) + }) + describe('> size not eql', () => { + it('> returns false', () => { + expect( + arrayOrderedEql([], [1]) + ).toBeFalse() + }) + }) + }) + }) }) diff --git a/deploy/saneUrl/index.js b/deploy/saneUrl/index.js index 2e9ab0b76528b999720781f7ff9cc913891f478d..72d0f9f9e2a8a13f98f96b7989c167ae61e2f47a 100644 --- a/deploy/saneUrl/index.js +++ b/deploy/saneUrl/index.js @@ -42,11 +42,16 @@ router.get('/:name', async (req, res) => { proxyStore.get(req, name) ]) - const { queryString, hashPath } = json + const { queryString, hashPath, ...rest } = json + + const xtraRoutes = [] + for (const key in rest) { + if (/^x-/.test(key)) xtraRoutes.push(`${key}:${name}`) + } if (redirectFlag) { if (queryString) return res.redirect(`${REAL_HOSTNAME}?${queryString}`) - if (hashPath) return res.redirect(`${REAL_HOSTNAME}#${hashPath}`) + if (hashPath) return res.redirect(`${REAL_HOSTNAME}#${hashPath}/${xtraRoutes.join('/')}`) } else { return res.status(200).send(json) } diff --git a/docs/releases/v2.5.8.md b/docs/releases/v2.5.8.md new file mode 100644 index 0000000000000000000000000000000000000000..c9b68931c30f63fcbd0a5ab32686719e3cc92b1c --- /dev/null +++ b/docs/releases/v2.5.8.md @@ -0,0 +1,9 @@ +# v2.5.8 + +## Bugfix + +- fixed user annotation with saneurl + +## Under the hood + +- remove unnecessary UI refreshes diff --git a/mkdocs.yml b/mkdocs.yml index 300b8ee5f336e62adc328f83b4f602b5dc894cf5..a2ade830ee78908a40bb8dcd9ee1b7b489753329 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,6 +40,7 @@ pages: - Fetching datasets: 'advanced/datasets.md' - Display non-atlas volumes: 'advanced/otherVolumes.md' - Release notes: + - v2.5.8: 'releases/v2.5.8.md' - v2.5.7: 'releases/v2.5.7.md' - v2.5.6: 'releases/v2.5.6.md' - v2.5.5: 'releases/v2.5.5.md' diff --git a/package.json b/package.json index 1b8c4de04b3f608d8cba63b6abc8f96120f17696..b538486fe3ccd90b081f90e8e2d462f80acbf90f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "interactive-viewer", - "version": "2.5.7", + "version": "2.5.8", "description": "HBP interactive atlas viewer. Integrating KG query, dataset previews & more. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular", "scripts": { "build-aot": "ng build && node ./third_party/matomo/processMatomo.js", diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts index 4b30e79372125b271161308125f9835400ec7886..5b1758d60906076840930cafbf878ab834e7e88d 100644 --- a/src/services/state/ngViewerState.store.ts +++ b/src/services/state/ngViewerState.store.ts @@ -191,29 +191,6 @@ export class NgViewerUseEffect implements OnDestroy { private http: HttpClient, ){ - // TODO either split backend user to be more granular, or combine the user config into a single subscription - this.subscriptions.push( - this.store$.pipe( - select('ngViewerState'), - debounceTime(200), - skip(1), - // Max frequency save once every second - - // properties to be saved - map(({ panelMode, panelOrder }) => { - return { panelMode, panelOrder } - }), - distinctUntilChanged(), - throttleTime(1000) - ).subscribe(ngViewerState => { - this.http.post(`${this.pureConstantService.backendUrl}user/config`, JSON.stringify({ ngViewerState }), { - headers: { - 'Content-type': 'application/json' - } - }) - }) - ) - this.applySavedUserConfig$ = this.http.get<TUserConfigResp>(`${this.pureConstantService.backendUrl}user/config`).pipe( map(json => { if (json.error) { diff --git a/src/ui/logoContainer/logoContainer.component.ts b/src/ui/logoContainer/logoContainer.component.ts index b7a85fda32a4147929b64e02be4f72c8481066b4..6b3ce834c7ae34d1459fc1965ab51dec8c14b3a1 100644 --- a/src/ui/logoContainer/logoContainer.component.ts +++ b/src/ui/logoContainer/logoContainer.component.ts @@ -1,6 +1,7 @@ import { Component } from "@angular/core"; import { PureContantService } from "src/util"; import { Subscription } from "rxjs"; +import { distinctUntilChanged } from "rxjs/operators"; @Component({ selector : 'logo-container', @@ -23,7 +24,9 @@ export class LogoContainer { private pureConstantService: PureContantService ){ this.subscriptions.push( - pureConstantService.darktheme$.subscribe(flag => { + pureConstantService.darktheme$.pipe( + distinctUntilChanged() + ).subscribe(flag => { this.containerStyle = { backgroundImage: `url('${this.pureConstantService.backendUrl}logo${!!flag ? '?darktheme=true' : ''}')` } diff --git a/src/util/pureConstant.service.spec.ts b/src/util/pureConstant.service.spec.ts index 82e3a591947131afe10f6449a06f7682f2211d3d..fa9ccf741bd443baf767d929d5786a08ecd190b7 100644 --- a/src/util/pureConstant.service.spec.ts +++ b/src/util/pureConstant.service.spec.ts @@ -5,7 +5,7 @@ import { MockStore, provideMockStore } from "@ngrx/store/testing" import { BS_ENDPOINT } from "src/atlasComponents/regionalFeatures/bsFeatures" import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service" import { viewerStateFetchedAtlasesSelector, viewerStateFetchedTemplatesSelector } from "src/services/state/viewerState/selectors" -import { PureContantService, SIIBRA_API_VERSION_HEADER_KEY } from "./pureConstant.service" +import { PureContantService, SIIBRA_API_VERSION_HEADER_KEY, SIIBRA_API_VERSION } from "./pureConstant.service" import { TAtlas } from "./siibraApiConstants/types" const MOCK_BS_ENDPOINT = `http://localhost:1234` @@ -68,7 +68,7 @@ describe('> pureConstant.service.ts', () => { const exp = httpController.expectOne(`${MOCK_BS_ENDPOINT}/atlases`) exp.flush([mockAtlas], { headers: { - [SIIBRA_API_VERSION_HEADER_KEY]: '0.1.7' + [SIIBRA_API_VERSION_HEADER_KEY]: SIIBRA_API_VERSION } }) service.allFetchingReady$.subscribe() diff --git a/src/util/pureConstant.service.ts b/src/util/pureConstant.service.ts index 4a700df39a6eb9eded56a548e3051ec175d72058..ba33d3b9388d8acbde91776d3826230be0074487 100644 --- a/src/util/pureConstant.service.ts +++ b/src/util/pureConstant.service.ts @@ -442,7 +442,6 @@ Raise/track issues at github repo: <a target = "_blank" href = "${this.repoUrl}" const { EXPERIMENTAL_FEATURE_FLAG } = environment if (EXPERIMENTAL_FEATURE_FLAG) return arr return arr - // return arr.filter(atlas => !/pre.?release/i.test(atlas.name)) }), shareReplay(1), ) diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts index 84621341979625d6ee5c4b866f405e940b886fbc..248b72fe0285b2e5510e948bebbe5f23da1d1f07 100644 --- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts +++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts @@ -1,12 +1,12 @@ import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, Inject, Optional } from "@angular/core"; import { fromEvent, Subscription, ReplaySubject, BehaviorSubject, Observable, race, timer, Subject } from 'rxjs' -import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip, tap } from "rxjs/operators"; +import { debounceTime, filter, map, scan, startWith, mapTo, switchMap, take, skip, tap, distinctUntilChanged } from "rxjs/operators"; import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; import { StateInterface as ViewerConfiguration } from "src/services/state/viewerConfig.store"; import { LoggingService } from "src/logging"; import { bufferUntil, getExportNehuba, getViewer, setNehubaViewer, switchMapWaitFor } from "src/util/fn"; import { NEHUBA_INSTANCE_INJTKN, scanSliceViewRenderFn } from "../util"; -import { deserialiseParcRegionId } from 'common/util' +import { deserialiseParcRegionId, arrayOrderedEql } from 'common/util' import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants"; import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service"; @@ -310,6 +310,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy { this.ondestroySubscriptions.push( this.layerVis$.pipe( switchMap(switchMapWaitFor({ condition: () => !!(this.nehubaViewer?.ngviewer) })), + distinctUntilChanged(arrayOrderedEql), debounceTime(160), ).subscribe((layerNames: string[]) => { /**