diff --git a/.eslintrc.js b/.eslintrc.js index fe0ec8f038dea3da56ed33293a2a6d6c14e5cd79..54af39fd8e639934ed7c4c2658f668a4facc8536 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,8 +20,9 @@ module.exports = { "requireLast": false } }], - "@typescript-eslint/no-unused-vars": ["warn", { - "argsIgnorePattern": "^_" + "@typescript-eslint/no-unused-vars": ["error", { + "argsIgnorePattern": "^_", + "ignoreRestSiblings": true }], "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-explicit-any": "off", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e092ca43f6bf407a44eda7316b426e95b71ceaa2..25a2cd761b2c38a48147a54f79640b34ad0fa7ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v2 + # - uses: actions/checkout@v3 # - uses: actions/setup-node@v1 # with: # node-version: '16.x' @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js 16.x for lint uses: actions/setup-node@v1 with: @@ -41,7 +41,7 @@ jobs: NODE_ENV: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js 16.x uses: actions/setup-node@v1 with: @@ -63,7 +63,7 @@ jobs: NODE_ENV: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Use Node.js 16.x uses: actions/setup-node@v1 with: diff --git a/.github/workflows/docker_img.yml b/.github/workflows/docker_img.yml index 5dd8db7891e24bf018b1f8e90164c038bf4b7b93..eb5368f03107d568f2157cbee31359bae1f03f6b 100644 --- a/.github/workflows/docker_img.yml +++ b/.github/workflows/docker_img.yml @@ -30,7 +30,7 @@ jobs: build: [ 'local', 'prod' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: 'Set matomo env var' # if matrix.build is local, only run if master or dev if: ${{ !(matrix.build == 'local' && github.ref != 'refs/heads/master' && github.ref != 'refs/heads/dev') }} @@ -120,7 +120,7 @@ jobs: needs: build-docker-img steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set env var run: | echo "Using github.ref: $GITHUB_REF" diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 9203ddec11d5972bd515d88cabe518281765a226..ec46c942aebec12804baf7c73da2bd092023d55c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -59,7 +59,7 @@ jobs: failure-state: ${{ steps.failure-state-step.outputs.failure-state }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: ${{ github.event.ref }} diff --git a/.github/workflows/manual_e2e.yml b/.github/workflows/manual_e2e.yml index 9829d7053bd047b73209192c0effbb5c87715599..a55bd1e16b0aee7d9dc6c91af8aba780b2bc40ec 100644 --- a/.github/workflows/manual_e2e.yml +++ b/.github/workflows/manual_e2e.yml @@ -9,7 +9,7 @@ jobs: hide_previous_if_exists: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: 'master' - uses: actions/github-script@v5 @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: 'Add checklist comment' uses: actions/github-script@v5 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 42fc00b29beb30be08b73526c02e9325fb3955ad..211fd6d9ed6b726d95894c16366837d1c743392a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: if: success() runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create Release id: create_release uses: actions/create-release@v1 diff --git a/.github/workflows/repo_sync_ebrains.yml b/.github/workflows/repo_sync_ebrains.yml index 548b768b0cd23b694a323919a7e64c71ee512d92..e29122fa0ac82239f8772e61b0d0b8581135fbd1 100644 --- a/.github/workflows/repo_sync_ebrains.yml +++ b/.github/workflows/repo_sync_ebrains.yml @@ -9,7 +9,7 @@ jobs: sync: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: wei/git-sync@v3 with: source_repo: ${GITHUB_REPOSITORY} diff --git a/common/constants.js b/common/constants.js index 0b92fb901782e568b9c8f2db52f9f7c9a79156b3..d3fe2b2b308520de3e5e9142d58b681c9dbc2a5a 100644 --- a/common/constants.js +++ b/common/constants.js @@ -163,5 +163,6 @@ If you do not accept the Terms & Conditions you are not permitted to access or u } exports.QUICKTOUR_DESC_MD = { + SLICE_VIEW: `The planar views allow you to zoom \`[mouse wheel]\`, pan the view \`[drag]\`, and select oblique sections \`<shift>\` + \`[drag]\`. You can \`[click]\` any brain regions to select them.` } })(typeof exports === 'undefined' ? module.exports : exports) diff --git a/deploy/app.js b/deploy/app.js index 6fa78cb8a4f51179509b26e5d807c89c05a9269b..f4b3dcd07ead1a7cad8f3c987db8619c1d16e219 100644 --- a/deploy/app.js +++ b/deploy/app.js @@ -17,7 +17,7 @@ app.use('/quickstart', require('./quickstart')) const hash = string => crypto.createHash('sha256').update(string).digest('hex') -app.use((req, _, next) => { +app.use((req, resp, next) => { if (/main\.bundle\.js$/.test(req.originalUrl)){ const xForwardedFor = req.headers['x-forwarded-for'] const ip = req.socket.remoteAddress @@ -28,6 +28,9 @@ app.use((req, _, next) => { ip: hash(ip) }) } + if (/favicon/.test(req.originalUrl)) { + resp.setHeader(`Cache-Control`, `public, max-age=604800, immutable`) + } next() }) diff --git a/deploy/csp/index.js b/deploy/csp/index.js index e976dd482d2845490dd24bebef212ea1ab0aeb17..e48f7f155e90e027e0274590f854d7281ca83bad 100644 --- a/deploy/csp/index.js +++ b/deploy/csp/index.js @@ -115,7 +115,7 @@ module.exports = { 'https://unpkg.com/d3@6.2.0/', // required for preview component 'https://unpkg.com/mathjax@3.1.2/', // math jax 'https://unpkg.com/three-surfer@0.0.13/dist/bundle.js', // for threeSurfer (freesurfer support in browser) - 'https://unpkg.com/ng-layer-tune@0.0.12/dist/ng-layer-tune/', // needed for ng layer control + 'https://unpkg.com/ng-layer-tune@0.0.13/dist/ng-layer-tune/', // needed for ng layer control 'https://unpkg.com/hbp-connectivity-component@0.6.6/', // needed for connectivity component (req, res) => res.locals.nonce ? `'nonce-${res.locals.nonce}'` : null, ...SCRIPT_SRC, diff --git a/docs/releases/v2.11.2.md b/docs/releases/v2.11.2.md new file mode 100644 index 0000000000000000000000000000000000000000..e541eb07514392266014c0442ea8acbd7a6f41a6 --- /dev/null +++ b/docs/releases/v2.11.2.md @@ -0,0 +1,19 @@ +# v2.11.2 + +## Features + +- Allow external layer control to persist state (#1338) + +## Bugfixes + +- Fixed point assignment for maps that do not have statistical maps. Also fixed error messages. +- Fixed neuron display +- Fixed typos in plugin API messages +- Fixed fsaverage toggle delineation only affecting hemisphere + +## Behind the scenes + +- Using hashed region name to encode selected region, rather than the archaic ngId & label index +- Pass messages to plugin API when not handled +- Fix lint +- Update CI diff --git a/e2e/checklist.md b/e2e/checklist.md index 50e2c7125d560b2ae8b8491a764a4e18f806f478..5e740efa83d8e722ee51853383ce0ece19bb25da 100644 --- a/e2e/checklist.md +++ b/e2e/checklist.md @@ -48,6 +48,20 @@ - [ ] v4 are visible - [ ] on hover, show correct region name(s) - [ ] whole mesh loads + +## Pt Assignments + +- [ ] human MNI152 julich brain should work (statistical) +- [ ] rat waxholm v4 should work (labelled) +- [ ] csv can be downloaded +- [ ] big brain & fsaverage *shouldn't* work + +## Download atlas + +- [ ] human MNI152 julich brain can be downloaded +- [ ] human MNI152 julich brain hoc1 left can be downloaded +- [ ] rat waxholm v4 can be downloaded + ## saneURL - [ ] saneurl generation functions properly - [ ] try existing key (human), and get unavailable error diff --git a/mkdocs.yml b/mkdocs.yml index df2d3dcc9969d727dc219a4a2450f0da70533330..fc88fe80be9e51f1591d52834ece4a8698b79ef5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -33,6 +33,7 @@ nav: - Fetching datasets: 'advanced/datasets.md' - Display non-atlas volumes: 'advanced/otherVolumes.md' - Release notes: + - v2.11.2: 'releases/v2.11.2.md' - v2.11.1: 'releases/v2.11.1.md' - v2.11.0: 'releases/v2.11.0.md' - v2.10.3: 'releases/v2.10.3.md' diff --git a/package-lock.json b/package-lock.json index eaa6b864e66c3e6d3556f6482a0816e0eb9e5d0c..792fff775330a11e4a0c83b26158e1694e7836f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "siibra-explorer", - "version": "2.10.0", + "version": "2.11.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "siibra-explorer", - "version": "2.10.0", + "version": "2.11.2", "license": "apache-2.0", "dependencies": { "@angular/animations": "^14.2.12", diff --git a/package.json b/package.json index 9719e8ac3d423f69b3e79bc3c266675522175327..4ea49304574b3072c6f16f5690b98a624b3558ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "siibra-explorer", - "version": "2.11.1", + "version": "2.11.2", "description": "siibra-explorer - explore brain atlases. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular", "scripts": { "lint": "eslint src --ext .ts", diff --git a/src/api/service.ts b/src/api/service.ts index 6adceb099682923f5306647b857346dca23b1aa3..44520f13f9a2958437463fb45793a925ee5b5aec 100644 --- a/src/api/service.ts +++ b/src/api/service.ts @@ -172,7 +172,7 @@ const broadCastDefault: BroadCastingApiEvents = { export class ApiService implements BoothResponder<ApiBoothEvents>{ - public broadcastCh = createBroadcastingJsonRpcChannel<`${NAMESPACE_TYPE}.on`, BroadCastingApiEvents>(`${namespace}.on`, broadCastDefault) + public broadcastCh = createBroadcastingJsonRpcChannel<`${NAMESPACE_TYPE}.on.`, BroadCastingApiEvents>(`${namespace}.on.`, broadCastDefault) public booth = new Booth<ApiBoothEvents>(this) private requestUserQueue: RequestUser<keyof RequestUserTypes>[] = [] @@ -263,27 +263,27 @@ export class ApiService implements BoothResponder<ApiBoothEvents>{ this.store.pipe( select(atlasSelection.selectors.selectedAtlas) ).subscribe(atlas => { - this.broadcastCh.emit('atlasSelected', translateV3Entities.retrieveAtlas(atlas)) + this.broadcastCh.emit('atlasSelected', atlas && translateV3Entities.retrieveAtlas(atlas)) }) this.store.pipe( select(atlasSelection.selectors.selectedParcellation) ).subscribe(parcellation => { - this.broadcastCh.emit('parcellationSelected', translateV3Entities.retrieveParcellation(parcellation)) + this.broadcastCh.emit('parcellationSelected', parcellation && translateV3Entities.retrieveParcellation(parcellation)) }) this.store.pipe( select(atlasSelection.selectors.selectedTemplate) ).subscribe(template => { - this.broadcastCh.emit('templateSelected', translateV3Entities.retrieveTemplate(template)) + this.broadcastCh.emit('templateSelected', template && translateV3Entities.retrieveTemplate(template)) }) this.store.pipe( select(atlasSelection.selectors.selectedRegions) ).subscribe(regions => { - this.broadcastCh.emit('regionsSelected', regions.map(reg => translateV3Entities.retrieveRegion(reg))) + this.broadcastCh.emit('regionsSelected', regions && regions.map(reg => translateV3Entities.retrieveRegion(reg))) }) this.store.pipe( select(atlasSelection.selectors.selectedParcAllRegions) ).subscribe(regions => { - this.broadcastCh.emit('allRegions', regions.map(reg => translateV3Entities.retrieveRegion(reg))) + this.broadcastCh.emit('allRegions', regions && regions.map(reg => translateV3Entities.retrieveRegion(reg))) }) this.store.pipe( select(atlasSelection.selectors.navigation) diff --git a/src/atlasComponents/sapi/core/index.ts b/src/atlasComponents/sapi/core/index.ts index a7fc4cbb696db25e9fa2159b5862e23c0bdb260e..ed68003d55ce542a35f5e173aaa173345202d597 100644 --- a/src/atlasComponents/sapi/core/index.ts +++ b/src/atlasComponents/sapi/core/index.ts @@ -1,4 +1,3 @@ export { SAPIAtlas } from "./sapiAtlas" -export { SAPISpace } from "./sapiSpace" export { SAPIParcellation } from "./sapiParcellation" export { SAPIRegion } from "./sapiRegion" diff --git a/src/atlasComponents/sapi/core/sapiRegion.ts b/src/atlasComponents/sapi/core/sapiRegion.ts index 0c5a2d0baf73fa0ba4d6e499abdbe4e3930eeb13..5c9db2f9fa82eed38c7c85afaebb57653bab0cee 100644 --- a/src/atlasComponents/sapi/core/sapiRegion.ts +++ b/src/atlasComponents/sapi/core/sapiRegion.ts @@ -1,7 +1,6 @@ import { SAPI } from ".."; import { SapiRegionModel, RouteParam } from "../typeV3"; -import { strToRgb, hexToRgb } from 'common/util' -import { NEVER, Observable, of } from "rxjs"; +import { Observable, of } from "rxjs"; import { map } from "rxjs/operators"; import { SAPIBase } from "./base"; import { SxplrRegion } from "../sxplrTypes"; @@ -92,16 +91,6 @@ export class SAPIRegion extends SAPIBase<RF>{ ) } - /** - * - * @deprecated - * @param volumeId - * @returns - */ - getVolumeInstance(volumeId: string): Observable<never> { - return NEVER - } - getDetail(spaceId: string): Observable<SapiRegionModel> { return this.sapi.v3Get("/regions/{region_id}", { path: { diff --git a/src/atlasComponents/sapi/core/sapiSpace.ts b/src/atlasComponents/sapi/core/sapiSpace.ts deleted file mode 100644 index b19359e3cbc373699aa332abb3b06d424d7f0742..0000000000000000000000000000000000000000 --- a/src/atlasComponents/sapi/core/sapiSpace.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Observable, of, throwError } from "rxjs" -import { SAPI } from '../sapi.service' -import { SxplrTemplate } from "../sxplrTypes" -import { map, switchMap } from "rxjs/operators" -import { SAPIBase } from "./base" - -/** - * All valid parcellation features - */ -const SpaceFeatures = { - Image: "Image", -} as const - -export type SF = keyof typeof SpaceFeatures - -type FeatureResponse = { - features: { - [key: string]: string - } -} - -type RegionalSpatialFeatureOpts = { - parcellationId: string - region: string -} - -type BBoxSpatialFEatureOpts = { - bbox: string -} - -type SpatialFeatureOpts = RegionalSpatialFeatureOpts | BBoxSpatialFEatureOpts - -export class SAPISpace extends SAPIBase<SF>{ - - static Features$ = of(Object.keys(SpaceFeatures) as SF[]) - public features$ = SAPISpace.Features$ - - constructor(private sapi: SAPI, public atlasId: string, public id: string){ - super(sapi) - this.prefix$ = SAPI.BsEndpoint$.pipe( - map(endpt => `${endpt}/atlases/${encodeURIComponent(this.atlasId)}/spaces/${encodeURIComponent(this.id)}`) - ) - } - - private prefix$: Observable<string> - - getModalities(): Observable<FeatureResponse> { - return this.prefix$.pipe( - switchMap(prefix => this.sapi.httpGet<FeatureResponse>( - `${prefix}/features`, - null, - )) - ) - } - - getDetail(): Observable<SxplrTemplate>{ - return this.prefix$.pipe( - switchMap(prefix => this.sapi.httpGet<SxplrTemplate>( - `${prefix}`, - null, - )) - ) - } - -} diff --git a/src/atlasComponents/sapi/index.ts b/src/atlasComponents/sapi/index.ts index 356276003c98cafb620eaf5aa67df35acffcff4b..2ec890861ec737a5a1aec4b1f6d4d899fac532c7 100644 --- a/src/atlasComponents/sapi/index.ts +++ b/src/atlasComponents/sapi/index.ts @@ -3,7 +3,6 @@ export { SAPIModule } from './module' export { SAPI } from "./sapi.service" export { SAPIAtlas, - SAPISpace, SAPIParcellation, SAPIRegion } from "./core" diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts index 7959d63fbd7715d7be672d626e02ffaa77d68100..bf83652addca6154bc509ef4ce7b3c25e743f752 100644 --- a/src/atlasComponents/sapi/sapi.service.ts +++ b/src/atlasComponents/sapi/sapi.service.ts @@ -1,7 +1,7 @@ import { Injectable } from "@angular/core"; import { HttpClient } from '@angular/common/http'; import { catchError, map, shareReplay, switchMap, take, tap } from "rxjs/operators"; -import { getExportNehuba } from "src/util/fn"; +import { getExportNehuba, noop } from "src/util/fn"; import { MatSnackBar } from "@angular/material/snack-bar"; import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; import { EnumColorMapName } from "src/util/colorMaps"; @@ -13,7 +13,6 @@ import { import { FeatureType, PathReturn, RouteParam, SapiRoute } from "./typeV3"; import { BoundingBox, SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate, VoiFeature, Feature } from "./sxplrTypes"; import { parcBanList, speciesOrder } from "src/util/constants"; -import { IDS } from "./constants"; export const useViewer = { THREESURFER: "THREESURFER", @@ -22,7 +21,7 @@ export const useViewer = { } as const export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version' -export const EXPECTED_SIIBRA_API_VERSION = '0.3.4' +export const EXPECTED_SIIBRA_API_VERSION = '0.3.5' let BS_ENDPOINT_CACHED_VALUE: Observable<string> = null @@ -111,8 +110,7 @@ export class SAPI{ .then(flag => { if (flag) rs(endpt) }) - // eslint-disable-next-line @typescript-eslint/no-empty-function - .catch(e => {}) + .catch(noop) } }) try { @@ -351,7 +349,7 @@ export class SAPI{ template.id, "LABELLED" ).pipe( - catchError((err, obs) => of(null as SxplrParcellation)), + catchError(() => of(null as SxplrParcellation)), map(_map => _map && parc) ) ) @@ -429,7 +427,7 @@ export class SAPI{ space.id, "LABELLED" ).pipe( - catchError((err, obs) => of(null as SxplrTemplate)), + catchError(() => of(null as SxplrTemplate)), map(_map => _map && space) ) ) @@ -520,7 +518,7 @@ export class SAPI{ // continue // } // } - for (const { volume: volumeIdx, fragment, label } of map.indices[regionname]) { + for (const { volume: volumeIdx, fragment } of map.indices[regionname]) { const { providedVolumes } = map.volumes[volumeIdx] if (!("neuroglancer/precomputed" in providedVolumes)) { continue diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts index e73a25736e182902ac13528a75848ee3a6f44e3c..3c10d79b9c849d9aa745879549de94cfe82b45fa 100644 --- a/src/atlasComponents/sapi/schemaV3.ts +++ b/src/atlasComponents/sapi/schemaV3.ts @@ -642,12 +642,6 @@ export interface components { */ ontologyIdentifier?: (string)[] } - /** - * ImageTypes - * @description An enumeration. - * @enum {unknown} - */ - ImageTypes: "BlockfaceVolumeOfInterest" | "CellBodyStainedVolumeOfInterest" | "CellbodyStainedSection" | "MRIVolumeOfInterest" | "PLIVolumeOfInterest" | "SegmentedVolumeOfInterest" | "XPCTVolumeOfInterest" /** LocationModel */ LocationModel: { /** @Type */ @@ -1237,12 +1231,6 @@ export interface components { /** Max */ max: number } - /** - * TabularTypes - * @description An enumeration. - * @enum {unknown} - */ - TabularTypes: "ReceptorDensityFingerprint" | "LayerwiseBigBrainIntensities" | "LayerwiseCellDensity" | "RegionalBOLD" /** ValidationError */ ValidationError: { /** Location */ @@ -1677,6 +1665,7 @@ export interface operations { parcellation_id: string space_id: string point: string + assignment_type?: string sigma_mm?: number } } @@ -1902,7 +1891,7 @@ export interface operations { query: { parcellation_id: string region_id: string - type?: components["schemas"]["TabularTypes"] + type?: string page?: number size?: number } @@ -1928,7 +1917,7 @@ export interface operations { query: { parcellation_id: string region_id: string - type?: components["schemas"]["TabularTypes"] + type?: string } path: { feature_id: string @@ -1955,7 +1944,7 @@ export interface operations { query: { space_id: string bbox?: string - type?: components["schemas"]["ImageTypes"] + type?: string page?: number size?: number } @@ -1980,7 +1969,7 @@ export interface operations { parameters: { query: { space_id: string - type?: components["schemas"]["ImageTypes"] + type?: string } path: { feature_id: string diff --git a/src/atlasComponents/sapi/sxplrTypes.ts b/src/atlasComponents/sapi/sxplrTypes.ts index ef556f25eb9c77344aa826d7b6d88e1e478c1227..95860602d584abe68740c2bbe9a5a4018e5e8641 100644 --- a/src/atlasComponents/sapi/sxplrTypes.ts +++ b/src/atlasComponents/sapi/sxplrTypes.ts @@ -102,10 +102,6 @@ export type Feature = { category?: string } & Partial<AdditionalInfo> -type DataFrame = { - index: Record<string, string> -} - export type VoiFeature = { bbox: BoundingBox ngVolume: { diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts index e92c14d9fe61a46e14b7bb4be99a1541c8e39ec8..da87a784b79e7d07e4b6848bbba43de822dc2138 100644 --- a/src/atlasComponents/sapi/translateV3.ts +++ b/src/atlasComponents/sapi/translateV3.ts @@ -40,7 +40,7 @@ class TranslateV3 { } async translateParcellation(parcellation:PathReturn<"/parcellations/{parcellation_id}">): Promise<SxplrParcellation> { const ds = await Promise.all((parcellation.datasets || []).map(ds => this.translateDs(ds))) - const { name, ...rest } = ds[0] || {} + const { ...rest } = ds[0] || {} const { ['@id']: prevId } = parcellation.version?.prev || {} return { id: parcellation["@id"], @@ -147,7 +147,7 @@ class TranslateV3 { for (const defaultImage of validImages) { const { providedVolumes } = defaultImage - const { "neuroglancer/precomputed": precomputedVol, ...rest } = await this.#extractNgPrecompUnfrag(providedVolumes) + const { "neuroglancer/precomputed": precomputedVol } = await this.#extractNgPrecompUnfrag(providedVolumes) if (!precomputedVol) { console.error(`neuroglancer/precomputed data source has not been found!`) diff --git a/src/atlasComponents/sapi/typeV3.ts b/src/atlasComponents/sapi/typeV3.ts index d65db66debe2bff385851d785213cec40749b3c5..22fdbcfe328e20fec2328f648be5baae4e1aa615 100644 --- a/src/atlasComponents/sapi/typeV3.ts +++ b/src/atlasComponents/sapi/typeV3.ts @@ -1,4 +1,4 @@ -import { components, paths, operations } from "./schemaV3" +import { components, paths } from "./schemaV3" export type SapiAtlasModel = PathReturn<"/atlases/{atlas_id}"> export type SapiSpaceModel = PathReturn<"/spaces/{space_id}"> @@ -22,6 +22,7 @@ type _FeatureType<FeatureRoute extends SapiRoute> = FeatureRoute extends `/featu ? never : FT extends "{feature_id}" ? never + // eslint-disable-next-line @typescript-eslint/no-unused-vars : FT extends `${infer _FT}/{${infer _FID}}` ? never : FT diff --git a/src/atlasComponents/sapiViews/core/parcellation/module.ts b/src/atlasComponents/sapiViews/core/parcellation/module.ts index 1dba15a672f32048508569aa03b9a63f9e96fed0..9fb7e0066b8d71826f96e61a38e88ad5e3157a6c 100644 --- a/src/atlasComponents/sapiViews/core/parcellation/module.ts +++ b/src/atlasComponents/sapiViews/core/parcellation/module.ts @@ -1,9 +1,7 @@ import { CommonModule } from "@angular/common"; -import { APP_INITIALIZER, NgModule } from "@angular/core"; -import { Store } from "@ngrx/store"; +import { NgModule } from "@angular/core"; import { ComponentsModule } from "src/components"; import { AngularMaterialModule } from "src/sharedModules"; -import { atlasAppearance } from "src/state"; import { StrictLocalModule } from "src/strictLocal"; import { DialogModule } from "src/ui/dialogInfo/module"; import { UtilModule } from "src/util"; @@ -14,6 +12,8 @@ import { ParcellationDoiPipe } from "./parcellationDoi.pipe"; import { ParcellationVisibilityService } from "./parcellationVis.service"; import { ParcellationGroupSelectedPipe } from "./parcellationGroupSelected.pipe"; import { IsGroupedParcellation } from "./isGroupedParcellation.pipe"; +import { EffectsModule } from "@ngrx/effects"; +import { ParcellationVisEffect } from "./parcellationVis.effect"; @NgModule({ imports: [ @@ -23,7 +23,10 @@ import { IsGroupedParcellation } from "./isGroupedParcellation.pipe"; UtilModule, SapiViewsUtilModule, DialogModule, - StrictLocalModule + StrictLocalModule, + EffectsModule.forFeature([ + ParcellationVisEffect + ]) ], declarations: [ FilterGroupedParcellationPipe, @@ -41,21 +44,6 @@ import { IsGroupedParcellation } from "./isGroupedParcellation.pipe"; ], providers: [ ParcellationVisibilityService, - { - provide: APP_INITIALIZER, - useFactory: (store: Store, svc: ParcellationVisibilityService) => { - svc.visibility$.subscribe(val => { - store.dispatch( - atlasAppearance.actions.setShowDelineation({ - flag: val - }) - ) - }) - return () => Promise.resolve() - }, - multi: true, - deps: [ Store, ParcellationVisibilityService ] - } ] }) diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationVis.effect.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationVis.effect.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc8d7765ba35cf9ed4fda375bc50bfaf36284ffa --- /dev/null +++ b/src/atlasComponents/sapiViews/core/parcellation/parcellationVis.effect.ts @@ -0,0 +1,14 @@ +import { Injectable } from "@angular/core"; +import { ParcellationVisibilityService } from "./parcellationVis.service"; +import { createEffect } from "@ngrx/effects"; +import { map } from "rxjs/operators"; +import { atlasAppearance } from "src/state"; + +@Injectable() +export class ParcellationVisEffect { + onEmitToggleDelineation = createEffect(() => this.svc.visibility$.pipe( + map(flag => atlasAppearance.actions.setShowDelineation({ flag })) + )) + + constructor(private svc: ParcellationVisibilityService){} +} diff --git a/src/atlasComponents/sapiViews/core/region/module.ts b/src/atlasComponents/sapiViews/core/region/module.ts index 9482528706f278dc726d6a17f8096ac82fed2328..6e9140d0b5968e384f4f08907246dd6b9a621a30 100644 --- a/src/atlasComponents/sapiViews/core/region/module.ts +++ b/src/atlasComponents/sapiViews/core/region/module.ts @@ -9,7 +9,6 @@ import { StrictLocalModule } from "src/strictLocal"; import { SapiViewsUtilModule } from "../../util/module"; import { SapiViewsCoreRegionRegionListItem } from "./region/listItem/region.listItem.component"; import { SapiViewsCoreRegionRegionBase } from "./region/region.base.directive"; -import { SapiViewsCoreRegionRegionalFeatureDirective } from "./region/region.features.directive"; import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.component"; @NgModule({ @@ -27,13 +26,11 @@ import { SapiViewsCoreRegionRegionRich } from "./region/rich/region.rich.compone SapiViewsCoreRegionRegionListItem, SapiViewsCoreRegionRegionRich, SapiViewsCoreRegionRegionBase, - SapiViewsCoreRegionRegionalFeatureDirective, ], exports: [ SapiViewsCoreRegionRegionListItem, SapiViewsCoreRegionRegionRich, SapiViewsCoreRegionRegionBase, - SapiViewsCoreRegionRegionalFeatureDirective, ] }) diff --git a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts index a2204a080335eb69d7644bf8c383eb4fb3d643f4..59b09ba91bb2e0eddbac28add6ed03fdd4c0cade 100644 --- a/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts +++ b/src/atlasComponents/sapiViews/core/region/region/region.base.directive.ts @@ -1,4 +1,4 @@ -import { Directive, EventEmitter, Input, Output, SimpleChanges } from "@angular/core"; +import { Directive, EventEmitter, Input, Output } from "@angular/core"; import { SxplrAtlas, SxplrParcellation, SxplrRegion, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"; import { translateV3Entities } from "src/atlasComponents/sapi/translateV3" import { rgbToHsl } from 'common/util' @@ -72,7 +72,7 @@ export class SapiViewsCoreRegionRegionBase { map(([ atp, region ]) => ({ ...atp, region })) ) - ngOnChanges(sc: SimpleChanges): void { + ngOnChanges(): void { const { atlas, template, parcellation } = this this.ATP$.next({ atlas, template, parcellation }) } @@ -97,7 +97,7 @@ export class SapiViewsCoreRegionRegionBase { */ const rgb = SAPIRegion.GetDisplayColor(this.region) || [200, 200, 200] this.regionRgbString = `rgb(${rgb.join(',')})` - const [_h, _s, l] = rgbToHsl(...rgb) + const [ /* _h */, /* _s */, l] = rgbToHsl(...rgb) this.regionDarkmode = l < 0.4 /** diff --git a/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts b/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts deleted file mode 100644 index b7019d477953329a64fb471ad620b4f7486b059f..0000000000000000000000000000000000000000 --- a/src/atlasComponents/sapiViews/core/region/region/region.features.directive.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Directive, OnChanges, SimpleChanges } from "@angular/core"; -import { BehaviorSubject, merge, NEVER } from "rxjs"; -import { switchMap, filter, startWith, shareReplay, scan } from "rxjs/operators"; -import { SAPI, SAPIRegion } from "src/atlasComponents/sapi"; -import { SxplrAtlas, SxplrParcellation, SxplrTemplate, SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes" -import { SapiViewsCoreRegionRegionBase } from "./region.base.directive"; - -@Directive({ - selector: '[sxplr-sapiviews-core-region-regional-feature]', - exportAs: 'sapiViewsRegionalFeature' -}) - -export class SapiViewsCoreRegionRegionalFeatureDirective extends SapiViewsCoreRegionRegionBase implements OnChanges{ - - constructor(sapi: SAPI){ - super(sapi) - } - - private features$ = NEVER - - public listOfFeatures$ = this.features$.pipe( - startWith([]), - shareReplay(1), - ) - - public busy$ = new BehaviorSubject<boolean>(false) -} diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts index 9db77ddb32c2077ff94f932785ffc5469a50b6a3..c61d33b9d2c25cd406383ed85cc437f6364748ca 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts @@ -28,8 +28,6 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase @Output('sxplr-sapiviews-core-region-region-rich-feature-clicked') featureClicked = new EventEmitter<Feature>() - public expandedPanel: string - constructor( sapi: SAPI, @Inject(DARKTHEME) public darktheme$: Observable<boolean>, @@ -41,16 +39,6 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase this.featureClicked.emit(feat) } - // eslint-disable-next-line @typescript-eslint/no-empty-function - handleExpansionPanelClosedEv(title: string){ - this.expandedPanel = null - } - - // eslint-disable-next-line @typescript-eslint/no-empty-function - handleExpansionPanelAfterExpandEv(title: string) { - this.expandedPanel = title - } - activePanelTitles$: Observable<string[]> = new Subject() private regionalStatisticalMaps$ = this.ATPR$.pipe( diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts index 6a8a8b546078f57907c0d7bf03bb8443f8d1bcb0..9f60eadca5c1111ad2ace1a72818e2ac8520feec 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/wrapper/wrapper.component.ts @@ -4,7 +4,7 @@ import { select, Store } from "@ngrx/store"; import { Observable, of, Subject, Subscription } from "rxjs"; import { filter, map, switchMap, tap, withLatestFrom } from "rxjs/operators"; import { SAPI } from "src/atlasComponents/sapi/sapi.service"; -import { atlasSelection } from "src/state"; +import { atlasAppearance, atlasSelection } from "src/state"; import { fromRootStore } from "src/state/atlasSelection"; import { DialogFallbackCmp } from "src/ui/dialogInfo"; import { DARKTHEME } from "src/util/injectionTokens"; @@ -16,11 +16,6 @@ function isATPGuard(obj: any): obj is Partial<ATP&{ requested: Partial<ATP> }> { return (obj.atlas || obj.template || obj.parcellation) && (!obj.requested || isATPGuard(obj.requested)) } -const banListParcName = new Set([ - "VEP Atlas", - "Desikan-Killiany 2006" -]) - @Component({ selector: 'sxplr-wrapper-atp-selector', templateUrl: './wrapper.template.html', @@ -60,7 +55,9 @@ export class WrapperATPSelector implements OnDestroy{ ) isBusy$ = new Subject<boolean>() - parcellationVisibility$ = this.svc.visibility$ + parcellationVisibility$ = this.store$.pipe( + select(atlasAppearance.selectors.showDelineation) + ) constructor( private dialog: MatDialog, diff --git a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html index 57a9cf3d5031587bc37f7ac36e115319b396f500..59694f74a9f84f067a93a56eb0f0d9988895d8e1 100644 --- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html +++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html @@ -1,7 +1,13 @@ -<div class="sxplr-m-2" *ngIf="busy$ | async"> +<div class="sxplr-m-2" *ngIf="busy$ | async as busyMessage"> <spinner-cmp class="sxplr-d-inline-block"></spinner-cmp> <span> - Performing probabilistic assignment ... + {{ busyMessage }} + </span> +</div> + +<div class="sxplr-m-2" *ngIf="error$ | async"> + <span> + An error occurred when performing map assignment. </span> </div> diff --git a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts index 90cf9b9ed7aaca875eec919e4a718e6a6ef66898..64b0ebdb1a71bdbf9baf0d0d4c687df6afa71cae 100644 --- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts +++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts @@ -1,15 +1,18 @@ -import { Component, Input, OnDestroy, Output, TemplateRef, EventEmitter, ViewChild, AfterViewInit } from '@angular/core'; +import { Component, Input, OnDestroy, Output, TemplateRef, EventEmitter } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { BehaviorSubject, EMPTY, Observable, Subscription, combineLatest, concat, of } from 'rxjs'; -import { map, shareReplay, switchMap, tap } from 'rxjs/operators'; +import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators'; import { SAPI, EXPECTED_SIIBRA_API_VERSION } from 'src/atlasComponents/sapi/sapi.service'; import { SxplrParcellation, SxplrRegion, SxplrTemplate } from 'src/atlasComponents/sapi/sxplrTypes'; import { translateV3Entities } from 'src/atlasComponents/sapi/translateV3'; import { PathReturn } from 'src/atlasComponents/sapi/typeV3'; -import { TSandsPoint } from 'src/util/types'; +import { TFace, TSandsPoint } from 'src/util/types'; import { TZipFileConfig } from "src/zipFilesOutput/type" import { environment } from "src/environments/environment" +const DOING_PROB_ASGMT = "Performing probabilistic assignment ..." +const DOING_LABEL_ASGMT = "Probabilistic assignment failed. Performing labelled assignment ..." + @Component({ selector: 'sxplr-point-assignment', templateUrl: './point-assignment.component.html', @@ -22,12 +25,19 @@ export class PointAssignmentComponent implements OnDestroy { "map value", ] - #busy$ = new BehaviorSubject(false) + #busy$ = new BehaviorSubject<string>(null) busy$ = this.#busy$.asObservable() + #error$ = new BehaviorSubject<string>(null) + error$ = this.#error$.asObservable() + #point = new BehaviorSubject<TSandsPoint>(null) @Input() - set point(val: TSandsPoint) { + set point(val: TSandsPoint|TFace) { + const { '@type': type } = val + if (type === "siibra-explorer/surface/face") { + return + } this.#point.next(val) } @@ -52,6 +62,9 @@ export class PointAssignmentComponent implements OnDestroy { this.#template, ]).pipe( switchMap(([ point, parcellation, template ]) => { + + this.#error$.next(null) + if (!point || !parcellation || !template) { return EMPTY } @@ -60,7 +73,7 @@ export class PointAssignmentComponent implements OnDestroy { console.warn(`point coordination space id ${ptSpaceId} is not the same as template id ${template.id}.`) return EMPTY } - this.#busy$.next(true) + this.#busy$.next(DOING_PROB_ASGMT) return concat( of(null), this.sapi.v3Get("/map/assign", { @@ -71,7 +84,24 @@ export class PointAssignmentComponent implements OnDestroy { sigma_mm: 0 } }).pipe( - tap(() => this.#busy$.next(false)), + catchError(() => { + this.#busy$.next(DOING_LABEL_ASGMT) + return this.sapi.v3Get("/map/assign", { + query: { + parcellation_id: parcellation.id, + point: point.coordinates.map(v => `${v.value/1e6}mm`).join(','), + space_id: template.id, + sigma_mm: 0, + assignment_type: "labelled" + } + }) + }), + catchError((err) => { + this.#busy$.next(null) + this.#error$.next(err.toString()) + return of(null) + }), + tap(() => this.#busy$.next(null)), ) ) }), @@ -118,7 +148,7 @@ export class PointAssignmentComponent implements OnDestroy { function generateReadMe(pt: TSandsPoint, parc: SxplrParcellation, tmpl: SxplrTemplate){ return `# Point assignment exporter -Exported by siibra-explorer verison \`${environment.VERSION}\` hash: \`${environment.GIT_HASH}\`. +Exported by siibra-explorer verison \`${environment.VERSION}\` hash: \`${environment.GIT_HASH.trim()}\`. On: ${new Date().toString()} diff --git a/src/atlasComponents/userAnnotations/module.ts b/src/atlasComponents/userAnnotations/module.ts index ec2586e5741ddc2d25ff4c45657154d882386946..f6c3d1682f6979a7fa2446a5f2a67c658f66d722 100644 --- a/src/atlasComponents/userAnnotations/module.ts +++ b/src/atlasComponents/userAnnotations/module.ts @@ -54,7 +54,7 @@ import { RoutedAnnotationService } from "./routedAnnotation.service"; // ... in url correctly { provide: APP_INITIALIZER, - useFactory:(svc: RoutedAnnotationService) => { + useFactory:(_svc: RoutedAnnotationService) => { return () => Promise.resolve() }, deps: [ RoutedAnnotationService ], diff --git a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts index 15bfc561e3f86608e4b18f0de67970d1391d8af5..09895109507b4eb9baf2283557cabd45ca7a59bc 100644 --- a/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts +++ b/src/atlasComponents/userAnnotations/singleAnnotationUnit/singleAnnotationUnit.component.ts @@ -44,7 +44,7 @@ export class SingleAnnotationUnit implements OnDestroy, AfterViewInit{ this.chSubs.push( this.formGrp.valueChanges.subscribe(value => { - const { name, desc, spaceId } = value + const { name, desc } = value this.managedAnnotation.name = name this.managedAnnotation.desc = desc }) diff --git a/src/atlasComponents/userAnnotations/tools/delete.ts b/src/atlasComponents/userAnnotations/tools/delete.ts index 2e118bcd3dddb46ab2aa8d6ce702b8b14ccdd054..be375acbc193ad1a6ec52e11c33ea75ee934fa4a 100644 --- a/src/atlasComponents/userAnnotations/tools/delete.ts +++ b/src/atlasComponents/userAnnotations/tools/delete.ts @@ -2,7 +2,7 @@ import { Directive, OnDestroy } from "@angular/core"; import { Observable, Subject, Subscription } from "rxjs"; import { filter, switchMapTo, takeUntil, withLatestFrom } from "rxjs/operators"; import { Point } from "./point"; -import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TNgAnnotationPoint, TToolType } from "./type"; +import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TToolType } from "./type"; @Directive() export class ToolDelete extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy { diff --git a/src/atlasComponents/userAnnotations/tools/point.ts b/src/atlasComponents/userAnnotations/tools/point.ts index a2e10804108682e14e844481094063cfac3fad5c..58d8cdde8acd05863e682814e37f8d667124e36e 100644 --- a/src/atlasComponents/userAnnotations/tools/point.ts +++ b/src/atlasComponents/userAnnotations/tools/point.ts @@ -1,4 +1,4 @@ -import { AbsToolClass, getCoord, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, INgAnnotationTypes, TAnnotationEvent, TBaseAnnotationGeomtrySpec, TCallbackFunction, TNgAnnotationEv, TSandsPoint, TToolType } from "./type"; +import { AbsToolClass, getCoord, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, INgAnnotationTypes, TAnnotationEvent, TBaseAnnotationGeomtrySpec, TCallbackFunction, TSandsPoint, TToolType } from "./type"; import { Observable, Subject, Subscription } from "rxjs"; import { Directive, OnDestroy } from "@angular/core"; import { filter, switchMapTo, takeUntil } from "rxjs/operators"; diff --git a/src/atlasComponents/userAnnotations/tools/poly.ts b/src/atlasComponents/userAnnotations/tools/poly.ts index 37a0fdbd397fe43fa136409af1503a2cb2443bbe..244c71a6a2d11163ba70af11727adcefd1ac399f 100644 --- a/src/atlasComponents/userAnnotations/tools/poly.ts +++ b/src/atlasComponents/userAnnotations/tools/poly.ts @@ -1,7 +1,7 @@ -import { IAnnotationTools, IAnnotationGeometry, TAnnotationEvent, IAnnotationEvents, AbsToolClass, INgAnnotationTypes, TNgAnnotationEv, TToolType, TBaseAnnotationGeomtrySpec, TSandsPolyLine, getCoord, TCallbackFunction } from "./type"; +import { IAnnotationTools, IAnnotationGeometry, TAnnotationEvent, IAnnotationEvents, AbsToolClass, INgAnnotationTypes, TToolType, TBaseAnnotationGeomtrySpec, TSandsPolyLine, getCoord, TCallbackFunction } from "./type"; import { Point, TPointJsonSpec } from './point' import { Directive, OnDestroy } from "@angular/core"; -import { merge, Observable, Subject, Subscription } from "rxjs"; +import { Observable, Subject, Subscription } from "rxjs"; import { filter, switchMapTo, takeUntil, withLatestFrom } from "rxjs/operators"; import { getUuid } from "src/util/fn"; @@ -129,7 +129,7 @@ export class Polygon extends IAnnotationGeometry{ } parseNgAnnotationObj(pickedAnnotationId: string, pickedOffset: number): { edge: [number, number], edgeIdx: number, point: Point, pointIdx: number } { - const [ id, edgeIdx, _shouldBeZero ] = pickedAnnotationId.split('_') + const [ id, edgeIdx, /* _shouldBeZero */ ] = pickedAnnotationId.split('_') if (id !== this.id) return null if (pickedOffset === 0) { diff --git a/src/atlasComponents/userAnnotations/tools/select.ts b/src/atlasComponents/userAnnotations/tools/select.ts index 10befd2dfe4b65ade055f2fcc81d57af5e2d4d3b..3653d86efb003f7676018b6adf53a5d7b40c80ef 100644 --- a/src/atlasComponents/userAnnotations/tools/select.ts +++ b/src/atlasComponents/userAnnotations/tools/select.ts @@ -2,7 +2,7 @@ import { Directive, OnDestroy } from "@angular/core"; import { Observable, Subject, Subscription } from "rxjs"; import { filter } from 'rxjs/operators' import { Point } from "./point"; -import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TNgAnnotationPoint, TToolType } from "./type"; +import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, IAnnotationTools, TAnnotationEvent, TCallbackFunction, TToolType } from "./type"; @Directive() export class ToolSelect extends AbsToolClass<Point> implements IAnnotationTools, OnDestroy { diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts index 4a18bd4f3dc806ea1bef95f1f6339a17b63f6ead..03f0f060e620f05897e380044ef381bea50c5c71 100644 --- a/src/atlasComponents/userAnnotations/tools/service.ts +++ b/src/atlasComponents/userAnnotations/tools/service.ts @@ -6,7 +6,7 @@ import { BehaviorSubject, combineLatest, fromEvent, merge, Observable, of, Subje import {map, switchMap, filter, shareReplay, pairwise, withLatestFrom } from "rxjs/operators"; import { NehubaViewerUnit } from "src/viewerModule/nehuba"; import { NEHUBA_INSTANCE_INJTKN } from "src/viewerModule/nehuba/util"; -import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TNgAnnotationLine, TCallback } from "./type"; +import { AbsToolClass, ANNOTATION_EVENT_INJ_TOKEN, IAnnotationEvents, IAnnotationGeometry, INgAnnotationTypes, INJ_ANNOT_TARGET, TAnnotationEvent, ClassInterface, TCallbackFunction, TSands, TGeometryJson, TCallback } from "./type"; import { getExportNehuba, switchMapWaitFor } from "src/util/fn"; import { Polygon } from "./poly"; import { Line } from "./line"; @@ -194,7 +194,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{ }): AbsToolClass<any>{ const { toolCls: Cls, target, editCmp } = arg const newTool = new Cls(this.annotnEvSubj, arg => this.handleToolCallback(arg)) as T & { ngOnDestroy?: () => void } - const { name, iconClass, onMouseMoveRenderPreview } = newTool + const { name, iconClass } = newTool this.moduleAnnotationTypes.push({ instance: newTool, diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index 9255c9065120627b7998d0c9c297972a69f1c0ff..c28d15f9a80133259294b4de407a543014adcf78 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -18,7 +18,7 @@ import { colorAnimation } from "./atlasViewer.animation" import { MouseHoverDirective } from "src/mouseoverModule"; import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar"; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; -import { ARIA_LABELS, CONST } from 'common/constants' +import { CONST } from 'common/constants' import { SlServiceService } from "src/spotlight/sl-service.service"; import { ClickInterceptorService } from "src/glue"; @@ -28,12 +28,6 @@ import { userPreference } from "src/state" import { DARKTHEME } from "src/util/injectionTokens"; import { EnumQuickTourSeverity } from "src/ui/quickTour/constrants"; -/** - * TODO - * check against auxlillary mesh indicies, to only filter out aux indicies - */ -const filterFn = (segment) => typeof segment.segment !== 'string' -const compareFn = (it, item) => it.name === item.name @Component({ selector: 'atlas-viewer', @@ -49,7 +43,6 @@ const compareFn = (it, item) => it.name === item.name export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public CONST = CONST - public compareFn = compareFn @ViewChild('cookieAgreementComponent', {read: TemplateRef}) public cookieAgreementComponent: TemplateRef<any> diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts index 67a422714822d25e47fdb55b345116cda9db8eba..32d6c3b05e767da8b43d6c019f3c7e440cdae729 100644 --- a/src/atlasViewer/atlasViewer.workerService.service.ts +++ b/src/atlasViewer/atlasViewer.workerService.service.ts @@ -34,7 +34,7 @@ export class AtlasWorkerService { ).toPromise() const { data: returnData } = message as MessageEvent - const { id, error, ...rest } = returnData + const { error, ...rest } = returnData if (error) { throw new Error(error.message || error) } diff --git a/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts b/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts index 2c5967a634ff77bd08cf6b46a920afdd6fc51c74..321e5f9bc6f52dbb1289af7ea04db2084d5a341d 100644 --- a/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts +++ b/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts @@ -1,4 +1,4 @@ -import { Directive, OnDestroy, Output, EventEmitter, Input, OnChanges, SimpleChanges, HostListener, ElementRef, HostBinding } from "@angular/core"; +import { Directive, OnDestroy, Output, EventEmitter, Input, OnChanges, SimpleChanges, HostBinding } from "@angular/core"; import { FabSpeedDialService } from "./fabSpeedDial.service"; import { Subscription } from "rxjs"; import { SCALE_ORIGIN } from './fabSpeedDial.service' diff --git a/src/components/flatHierarchy/spacer.pipe.ts b/src/components/flatHierarchy/spacer.pipe.ts index a7533b68c759ed72c1a56b87a762409a4d8a5f2b..73498906a0e6aa52ac93a16027c59e20f30b7c0d 100644 --- a/src/components/flatHierarchy/spacer.pipe.ts +++ b/src/components/flatHierarchy/spacer.pipe.ts @@ -7,7 +7,7 @@ import { TreeNode } from "./const" }) export class FlatHierarchySpacer implements PipeTransform{ - public transform<T>(inputNode: TreeNode<T>, ...args: any[]) { + public transform<T>(inputNode: TreeNode<T>, ..._args: any[]) { return Array(inputNode.level).fill(null) } } diff --git a/src/components/flatHierarchy/treeView/treeControl.ts b/src/components/flatHierarchy/treeView/treeControl.ts index 7471dfbb839cb36c23413d026c2f9c8f607af6dd..d8676d4ff1b7840994f9ac12bebed0ab5c8d8e31 100644 --- a/src/components/flatHierarchy/treeView/treeControl.ts +++ b/src/components/flatHierarchy/treeView/treeControl.ts @@ -13,7 +13,7 @@ export class Tree<T extends Record<string, unknown>>{ return this._nodes } - private _isParent: IsParent<T> = (c, p) => false + private _isParent: IsParent<T> = (_c, _p) => false protected set isParent(fn: IsParent<T>){ if (fn === this._isParent) return this._isParent = fn @@ -81,7 +81,7 @@ export class Tree<T extends Record<string, unknown>>{ constructor( _nodes: T[] = [], - _isParent: IsParent<T> = (c, p) => false + _isParent: IsParent<T> = (_c, _p) => false ){ this._nodes = _nodes this._isParent = _isParent diff --git a/src/components/markdown/markdownCmp/markdown.component.ts b/src/components/markdown/markdownCmp/markdown.component.ts index 7084626a5ed37fb4162d610edfe232fcf3852381..fd6776dec0c312e74c659bff8fb4668e2fbf130a 100644 --- a/src/components/markdown/markdownCmp/markdown.component.ts +++ b/src/components/markdown/markdownCmp/markdown.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, AfterViewChecked, OnChanges, SecurityContext } from '@angular/core' +import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ChangeDetectorRef, OnChanges, SecurityContext } from '@angular/core' import { DomSanitizer } from '@angular/platform-browser' import * as showdown from 'showdown' diff --git a/src/components/parseAttribute.directive.ts b/src/components/parseAttribute.directive.ts deleted file mode 100644 index 34a9b124841c32106a8f590f00d80d69f85d337b..0000000000000000000000000000000000000000 --- a/src/components/parseAttribute.directive.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Directive, OnChanges, SimpleChanges } from "@angular/core"; - -// TODO deprecate this directive -function parseAttribute(arg: any, expectedType: string) { - - // if( - // typeof arg === expectedType || - // arg === undefined || - // arg === null - // ){ - // return arg - // } - // switch (expectedType){ - // case 'object': - // try{ - // const json = JSON.parse(arg) - // return json - // }catch(e){ - // this.log.warn('parseAttribute error, cannot JSON.parse object') - // return arg - // } - // case 'boolean' : - // return arg === 'true' - // case 'number': - // return isNaN(arg) ? 0 : Number(arg) - - // case 'string': - // default : - // return arg - // } - - /* return if empty string */ - if ( - arg === '' || - arg === undefined || - arg === null - ) { - return arg - } - - if (!isNaN(arg)) { - return Number(arg) - } - - if (arg === 'true') { - return true - } - - if (arg === 'false') { - return false - } - - try { - const json = JSON.parse(arg) - return json - } catch (e) { - // this.log.warn('parseAttribute, parse JSON, not a json') - /* not a json, continue */ - /* probably print in debug mode */ - } - - return arg -} - -@Directive({ - selector : '[ivparseattribute]', -}) - -export class ParseAttributeDirective implements OnChanges { - public ngOnChanges(simpleChanges: SimpleChanges) { - Object.keys(simpleChanges).forEach(key => { - this[key] = parseAttribute(simpleChanges[key].currentValue, typeof simpleChanges[key].previousValue) - }) - } -} diff --git a/src/components/smartChip/component/smartChip.component.ts b/src/components/smartChip/component/smartChip.component.ts index b68cfb42769e31067b42614351e8db04717fdcf7..99eed11983dd317c32c5f4875e93f640caf8bf95 100644 --- a/src/components/smartChip/component/smartChip.component.ts +++ b/src/components/smartChip/component/smartChip.component.ts @@ -13,12 +13,12 @@ const cssColorToHsl = (input: string) => { parseInt(match[2]), parseInt(match[3]), ] - const [_h, _s, l] = rgbToHsl(...rgb) + const [ /* _h */, /* _s */, l] = rgbToHsl(...rgb) return l } if (/hsl/i.test(input)) { const match = /\((.*)\)/.exec(input) - const [h, s, l] = match[1].split(",") + const [ /* _h */, /* _s */, l] = match[1].split(",") const trimmedL = l.trim() if (/%$/.test(trimmedL)) { const match = /^([0-9]+)%/.exec(trimmedL) @@ -27,7 +27,7 @@ const cssColorToHsl = (input: string) => { } if (/^#/i.test(input) && input.length === 7) { const [r, g, b] = hexToRgb(input) - const [_h, _s, l] = rgbToHsl(r, g, b) + const [ /*_h */, /*_s */, l] = rgbToHsl(r, g, b) return l } throw new Error(`Cannot parse css color: ${input}`) diff --git a/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts index f4a2c8e6231db8386047195264206516136d8577..77b69d99dccae4d04870b68d7ac6a223b59548df 100644 --- a/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts +++ b/src/features/connectivity/connectivityBrowser/connectivityBrowser.component.ts @@ -1,7 +1,7 @@ import { Component, ElementRef, OnDestroy, ViewChild, Input, SimpleChanges, HostListener, OnChanges } from "@angular/core"; import { Store, select} from "@ngrx/store"; import { Subscription, BehaviorSubject, combineLatest, merge, concat, NEVER} from "rxjs"; -import { switchMap, map, tap, shareReplay, distinctUntilChanged, withLatestFrom, filter, finalize } from "rxjs/operators"; +import { switchMap, map, shareReplay, distinctUntilChanged, withLatestFrom, filter, finalize } from "rxjs/operators"; import { atlasAppearance, atlasSelection } from "src/state"; import { SAPI } from "src/atlasComponents/sapi/sapi.service"; diff --git a/src/features/entry/entry.component.ts b/src/features/entry/entry.component.ts index 722fd8e99735fb88a04ab0435c6a0bccaa760306..610af8724d495501def8df5fc6ead5e761f9b17f 100644 --- a/src/features/entry/entry.component.ts +++ b/src/features/entry/entry.component.ts @@ -14,7 +14,7 @@ import { TranslatedFeature } from '../list/list.directive'; const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => { const returnVal: Record<string, T[]> = {} for (const item of categories) { - const { category, ...rest } = item + const { category } = item if (!category) continue if (typeof category !== "string") continue if (!returnVal[category]) { @@ -128,7 +128,6 @@ export class EntryComponent extends FeatureBase implements AfterViewInit, OnDest await ds.pull() } catch (e) { if (e instanceof DsExhausted) { - console.log('exhausted') break } if (e instanceof IsAlreadyPulling ) { diff --git a/src/index.html b/src/index.html index c1cef937495c9ed9218e6ec90326307b07a8816e..1ed2d8187899e51442bc39c8ab9305ac15c7ea0f 100644 --- a/src/index.html +++ b/src/index.html @@ -14,7 +14,7 @@ <script src="extra_js.js"></script> <script src="https://unpkg.com/kg-dataset-previewer@1.2.0/dist/kg-dataset-previewer/kg-dataset-previewer.js" defer></script> <script src="https://unpkg.com/three-surfer@0.0.13/dist/bundle.js" defer></script> - <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.12/dist/ng-layer-tune/ng-layer-tune.esm.js"></script> + <script type="module" src="https://unpkg.com/ng-layer-tune@0.0.13/dist/ng-layer-tune/ng-layer-tune.esm.js"></script> <script type="module" src="https://unpkg.com/hbp-connectivity-component@0.6.6/dist/connectivity-component/connectivity-component.js" ></script> <script defer src="https://unpkg.com/mathjax@3.1.2/es5/tex-svg.js"></script> <script defer src="https://unpkg.com/d3@6.2.0/dist/d3.min.js"></script> diff --git a/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts b/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts index 6f909d5280166c3544c8f3ec81ed0610db49fa88..1b39b366a26ad75ffd55b1a2af12fc78679dd20e 100644 --- a/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts +++ b/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts @@ -171,13 +171,11 @@ export class KeyFrameCtrlCmp implements OnDestroy { ) const startVec = vec1.clone() - const vec1Length = vec1.length() const vec2 = new THREE.Vector3( toPayloadCamera.x, toPayloadCamera.y, toPayloadCamera.z, ) - const vec2Length = vec2.length() vec1.normalize() vec2.normalize() diff --git a/src/messaging/nmvSwc/index.ts b/src/messaging/nmvSwc/index.ts index d7bd4d6a767d013e2e357cb98d79202a7ecfed08..87e4b10ef6a82fee3b44926a4e7e02684274433c 100644 --- a/src/messaging/nmvSwc/index.ts +++ b/src/messaging/nmvSwc/index.ts @@ -86,12 +86,6 @@ const getAtlas = (spaceId: SPACE_ID) => { return DEFAULT_ATLAS[spaceId] } -const getVoxelFromSpace = (spaceId: string) => { - for (const key in NM_IDS){ - if (NM_IDS[key] === spaceId) return IAV_VOXEL_SIZES_NM[key] - } - return null -} export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagingActions<keyof IMessagingActionTmpl>> => { const subject = new Subject<IMessagingActions<keyof IMessagingActionTmpl>>() @@ -156,27 +150,16 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi ) const uuid = getUuid() - // NG internal treats skeleton as mm - const voxelSize = getVoxelFromSpace(toSpace) - /** - * swc seem to scale with voxelSize... strangely enough - * voxelSize nm / voxel -> goal is 1 voxel/um - * 1e3 / voxelSize - */ - const scaleUmToVoxelFixed = [ - voxelSize[0], - voxelSize[1], - voxelSize[2], - ] // NG translation works on nm scale const scaleUmToNm = 1e3 const modA = mat3.fromValues( - scaleUmToVoxelFixed[0], 0, 0, - 0, scaleUmToVoxelFixed[1], 0, - 0, 0, scaleUmToVoxelFixed[2] + scaleUmToNm, 0, 0, + 0, scaleUmToNm, 0, + 0, 0, scaleUmToNm ) mat3.mul(modA, modA, [...A[0], ...A[1], ...A[2]]) - const modb = vec3.scale(vec3.create(), b, scaleUmToNm) + const modb = vec3.mul(vec3.create(), b, [ scaleUmToNm, scaleUmToNm, scaleUmToNm]) + vec3.scale(vec3.create(), b, scaleUmToNm) const transform = [ [...modA.slice(0, 3), modb[0]] as TVec4, [...modA.slice(3, 6), modb[1]] as TVec4, diff --git a/src/messaging/service.spec.ts b/src/messaging/service.spec.ts index 8ba8cad4e3cdaf0d776fd5ff2f1e1cc0958c8b08..05da2a3999f7c350a304b3fca3e997029bec624e 100644 --- a/src/messaging/service.spec.ts +++ b/src/messaging/service.spec.ts @@ -1,5 +1,5 @@ import { TestBed } from "@angular/core/testing" -import { MockStore, provideMockStore } from "@ngrx/store/testing" +import { provideMockStore } from "@ngrx/store/testing" import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service" import { AngularMaterialModule } from "src/sharedModules" import { getUuid } from "src/util/fn" @@ -9,6 +9,7 @@ import { MANAGED_METHODS } from './service' import { of, Subject } from "rxjs" import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component" import { TYPE as NATIVE_TYPE } from './native' +import { ApiService } from "src/api" describe('> service.ts', () => { describe('> MessagingService', () => { @@ -34,6 +35,17 @@ describe('> service.ts', () => { { provide: WINDOW_MESSAGING_HANDLER_TOKEN, useValue: windowMessagehandler + }, + { + provide: ApiService, + useValue: { + booth: { + handshake(){} + }, + broadcastCh: { + addListener(){} + } + } } ] }) @@ -118,7 +130,7 @@ describe('> service.ts', () => { it('> pong', async () => { const result = await mService.handleMessage({ data: { - method: `${IAV_POSTMESSAGE_NAMESPACE}ping` + method: `ping` }, origin: 'foobar' }) diff --git a/src/messaging/service.ts b/src/messaging/service.ts index 365a2d1880bd4513bb088b49ff0b761faa30b36c..3abdeb901b8c1a2f713e1eb4bdb1cef5a38fac27 100644 --- a/src/messaging/service.ts +++ b/src/messaging/service.ts @@ -1,29 +1,44 @@ import { Inject, Injectable, Optional } from "@angular/core"; import { Observable } from "rxjs"; import { MatDialog } from "@angular/material/dialog"; -import { MatSnackBar } from "@angular/material/snack-bar"; - -import { getUuid } from "src/util/fn"; -import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"; +import { getUuid, noop } from "src/util/fn"; import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDialog.component"; import { IMessagingActions, IMessagingActionTmpl, WINDOW_MESSAGING_HANDLER_TOKEN, IWindowMessaging } from './types' import { TYPE as NMV_TYPE, processJsonLd as nmvProcess } from './nmvSwc/index' import { TYPE as NATIVE_TYPE, processJsonLd as nativeProcess } from './native' +import { BoothVisitor, JRPCRequest, ListenerChannel } from "src/api/jsonrpc" +import { ApiService } from "src/api"; +import { ApiBoothEvents, namespace as apiNameSpace } from "src/api/service"; export const IAV_POSTMESSAGE_NAMESPACE = `ebrains:iav:` +const RECOGNISED_NAMESPACES = [ + IAV_POSTMESSAGE_NAMESPACE, + apiNameSpace +] + export const MANAGED_METHODS = [ 'openminds:nmv:loadSwc', 'openminds:nmv:unloadSwc' ] +class WindowOpenerListener implements ListenerChannel { + constructor( + public registerLeaveCb: () => void, + public notify: (payload: JRPCRequest<unknown, unknown>) => void + ){} + +} + @Injectable({ providedIn: 'root' }) export class MessagingService { + private originListenerMap = new Map<string, {listener: WindowOpenerListener, visitor: BoothVisitor<ApiBoothEvents>}>() + private whiteListedOrigins = new Set() private pendingRequests: Map<string, Promise<boolean>> = new Map() private windowName: string @@ -32,8 +47,7 @@ export class MessagingService { constructor( private dialog: MatDialog, - private snackbar: MatSnackBar, - private worker: AtlasWorkerService, + private apiService: ApiService, @Optional() @Inject(WINDOW_MESSAGING_HANDLER_TOKEN) private messagingHandler: IWindowMessaging, ){ @@ -59,12 +73,45 @@ export class MessagingService { } window.addEventListener('message', async ({ data, origin, source }) => { + + /** + * only deal with opener + */ + if (!window.opener || source !== window.opener) { + return + } + if (/^webpack/.test(data.type)) return - if (data === '') return + if (!data) return + const { method } = data + if (RECOGNISED_NAMESPACES.every(namespace => (method || '').indexOf(namespace) !== 0)) { + return + } const src = source as Window const { id } = data try { const result = await this.handleMessage({ data, origin }) + if (!this.originListenerMap.has(origin)) { + const listener = new WindowOpenerListener( + noop, + val => src.postMessage(val, origin) + ) + + const visitor = this.apiService.booth.handshake() + this.originListenerMap.set(origin, {listener, visitor}) + + this.apiService.broadcastCh.addListener(listener) + + + /** + * if result was not yet populated, try populating it with + * siibra-explorer api + */ + } + if (!result) { + const { visitor } = this.originListenerMap.get(origin) + return await visitor.request(data) + } src.postMessage({ id, jsonrpc: '2.0', @@ -94,27 +141,25 @@ export class MessagingService { public async handleMessage({ data, origin }) { const { method, param } = data - - if (!method) return - if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return - const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '') - /** * if ping method, respond pong method */ - if (strippedMethod === 'ping') { + if (method === 'ping') { return 'pong' } /** * otherwise, check permission */ - const allow = await this.checkOrigin({ origin }) if (!allow) throw ({ code: 403, message: 'User declined' }) + + if (!method) return + if (method.indexOf(IAV_POSTMESSAGE_NAMESPACE) !== 0) return + const strippedMethod = method.replace(IAV_POSTMESSAGE_NAMESPACE, '') // TODO // in future, check if in managed_methods @@ -169,50 +214,11 @@ export class MessagingService { // TODO combine api service and messaging service into one // and implement it properly - // if (method === 'viewerHandle:add3DLandmarks') { - // this.apiService.interactiveViewer.viewerHandle.add3DLandmarks(param) - // return 'OK' - // } - - // if (method === 'viewerHandle:remove3DLandmarks') { - // this.apiService.interactiveViewer.viewerHandle.remove3DLandmarks(param) - // return 'OK' - // } - - /** - * TODO use loadResource in the future - */ - if (method === '_tmp:plotly') { - const isLoadingSnack = this.snackbar.open(`Loading plotly mesh ...`) - const resp = await this.worker.sendMessage({ - method: `PROCESS_PLOTLY`, - param - }) - isLoadingSnack?.dismiss() - const meshId = 'bobby' - /** - * TODO re-enable plotly VTK mesh - */ - - // if (false) { - // const { objectUrl, customFragmentColor } = resp.result || {} - // this.loadMesh({ - // type: 'VTK', - // id: meshId, - // url: objectUrl, - // customFragmentColor - // }) - // } else { - // this.snackbar.open(`Error: loadMesh method not injected.`) - // } - return 'OK' - } - if (MANAGED_METHODS.indexOf(method) >= 0) { return await this.processJsonld(param) } - throw ({ code: 404, message: 'Method not found' }) + return } async checkOrigin({ origin }): Promise<boolean> { diff --git a/src/mouseoverModule/type.ts b/src/mouseoverModule/type.ts deleted file mode 100644 index 38033f903e9047b4164c90137564a19d0f75ae86..0000000000000000000000000000000000000000 --- a/src/mouseoverModule/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { TRegionSummary } from "src/util/siibraApiConstants/types"; - -export type TMouseOverVtkLandmark = { - landmarkName: string -} \ No newline at end of file diff --git a/src/plugin/plugin.module.ts b/src/plugin/plugin.module.ts index 998ca4056ecbb7a069133b021bc3e0aab8605fdc..563a7d9dbb010c2e5654ff7631e1168098c52919 100644 --- a/src/plugin/plugin.module.ts +++ b/src/plugin/plugin.module.ts @@ -1,10 +1,9 @@ -import { CommonModule, DOCUMENT } from "@angular/common"; +import { CommonModule } from "@angular/common"; import { HttpClientModule } from "@angular/common/http"; import { NgModule } from "@angular/core"; import { LoggingModule } from "src/logging"; import { AngularMaterialModule } from "src/sharedModules"; import { UtilModule } from "src/util"; -import { appendScriptFactory, APPEND_SCRIPT_TOKEN, removeScriptFactory, REMOVE_SCRIPT_TOKEN } from "src/util/constants"; import { IFrameSrcPipe } from "./iframeSrc.pipe"; import { PluginBannerUI } from "./pluginBanner/pluginBanner.component"; import { PluginPortal } from "./pluginPortal/pluginPortal.component"; diff --git a/src/routerModule/routeStateTransform.service.spec.ts b/src/routerModule/routeStateTransform.service.spec.ts index a341cdd6d405e275beedfd82a29c8f42ef3f36a5..2dee59c937f86923ca06f41c073aca2a22dcc0ba 100644 --- a/src/routerModule/routeStateTransform.service.spec.ts +++ b/src/routerModule/routeStateTransform.service.spec.ts @@ -6,6 +6,7 @@ import { DefaultUrlSerializer } from "@angular/router" import * as nehubaConfigService from "src/viewerModule/nehuba/config.service" import { atlasSelection, userInteraction } from "src/state" import { encodeNumber } from "./cipher" +import { QuickHash } from "src/util/fn" const serializer = new DefaultUrlSerializer() @@ -123,7 +124,7 @@ describe("> routeStateTransform.service.ts", () => { const altasObj = {"@id": 'foo-bar-a'} const templObj = {"@id": 'foo-bar-t'} const parcObj = {"@id": 'foo-bar-p'} - const regions = [{}] + const regions = [{'name': 'selected-region-1'}] const standAloneVolumes = [] const navigation = null @@ -181,9 +182,12 @@ describe("> routeStateTransform.service.ts", () => { const getRegionLabelIndicesSpy = sapi.getRegionLabelIndices as jasmine.Spy getRegionLabelIndicesSpy.and.resolveTo(labelIndex) - const s = await svc.cvtStateToRoute(state as any) - expect(s).toContain(`r:${ngId}::${encodeNumber(labelIndex, { float: false })}`) + + expect(getParcNgId).not.toHaveBeenCalled() + expect(getRegionLabelIndicesSpy).not.toHaveBeenCalled() + + expect(s).toContain(`rn:${QuickHash.GetHash(regions[0].name)}`) }) it('> ngId containing expected value', async () => { diff --git a/src/routerModule/routeStateTransform.service.ts b/src/routerModule/routeStateTransform.service.ts index 1032448ec67ea1c9bf80d3f18f82f901bf7c6057..b01ac54beb7085b01ea548ac2de69b4d0a103fff 100644 --- a/src/routerModule/routeStateTransform.service.ts +++ b/src/routerModule/routeStateTransform.service.ts @@ -9,6 +9,7 @@ import { getParcNgId } from "src/viewerModule/nehuba/config.service"; import { decodeToNumber, encodeNumber, encodeURIFull, separator } from "./cipher"; import { TUrlAtlas, TUrlPathObj, TUrlStandaloneVolume } from "./type"; import { decodePath, encodeId, decodeId, encodePath } from "./util"; +import { QuickHash } from "src/util/fn"; @Injectable() export class RouteStateTransformSvc { @@ -26,6 +27,7 @@ export class RouteStateTransformSvc { const selectedTemplateId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.t) ) const selectedParcellationId = decodeId( RouteStateTransformSvc.GetOneAndOnlyOne(obj.p) ) const selectedRegionIds = obj.r + const selectedRegionNames = obj.rn if (!selectedAtlasId || !selectedTemplateId || !selectedParcellationId) { return {} @@ -60,7 +62,11 @@ export class RouteStateTransformSvc { const userViewer = await this.sapi.useViewer(selectedTemplate).toPromise() const selectedRegions = await (async () => { - if (!selectedRegionIds) return [] + if (!selectedRegionIds && !selectedRegionNames) return [] + + if (selectedRegionNames && selectedRegionNames.length > 0) { + return allParcellationRegions.filter(region => selectedRegionNames.includes(QuickHash.GetHash(region.name))) + } /** * should account for @@ -268,16 +274,7 @@ export class RouteStateTransformSvc { } } - // encoding selected regions - let selectedRegionsString: string - if (selectedRegions.length === 1) { - const region = selectedRegions[0] - const labelIndex = await this.sapi.getRegionLabelIndices(selectedTemplate, selectedParcellation, region) - - const ngId = getParcNgId(selectedAtlas, selectedTemplate, selectedParcellation, region) - selectedRegionsString = `${ngId}::${encodeNumber(labelIndex, { float: false })}` - } - let routes: TUrlPathObj<string, TUrlAtlas<string>> | TUrlPathObj<string, TUrlStandaloneVolume<string>> + let routes: TUrlPathObj<string|string[], TUrlAtlas<string|string[]>> | TUrlPathObj<string, TUrlStandaloneVolume<string>> routes = { // for atlas @@ -287,7 +284,8 @@ export class RouteStateTransformSvc { // for parcellation p: selectedParcellation && encodeId(selectedParcellation.id), // for regions - r: selectedRegionsString && encodeURIFull(selectedRegionsString), + // r: selectedRegionsString && encodeURIFull(selectedRegionsString), + rn: selectedRegions[0] && selectedRegions.map(r => QuickHash.GetHash(r.name)), // nav ['@']: cNavString, // showing dataset diff --git a/src/routerModule/type.ts b/src/routerModule/type.ts index 13205ad3e09e0969a8c419a5d9803b2883bc2de3..284b5e7a9441fd52be9281ad3dec5c749e390993 100644 --- a/src/routerModule/type.ts +++ b/src/routerModule/type.ts @@ -7,6 +7,7 @@ export type TUrlAtlas<T> = { t: T // template p: T // parcellation r?: T // region selected + rn?: T } export type TUrlPlugin<T> = { diff --git a/src/screenshot/screenshotCmp/screenshot.component.ts b/src/screenshot/screenshotCmp/screenshot.component.ts index 9af9d6b90a7d60af3438b2784906918b6f74aab0..2ccb1da45087f34c917a7626827ae14759cd48db 100644 --- a/src/screenshot/screenshotCmp/screenshot.component.ts +++ b/src/screenshot/screenshotCmp/screenshot.component.ts @@ -2,7 +2,7 @@ import { Component, HostListener, Inject, OnDestroy, Output, EventEmitter, Optio import { MatDialog } from "@angular/material/dialog"; import { MatSnackBar } from "@angular/material/snack-bar"; import { combineLatest, Observable, Subject, Subscription } from "rxjs"; -import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, tap } from "rxjs/operators"; +import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo } from "rxjs/operators"; import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../util"; import { getDateString } from 'common/util' import { getUuid } from "src/util/fn"; diff --git a/src/services/uiService.service.ts b/src/services/uiService.service.ts index cc8774728fb1742e36722135960341c35f7c0599..2e6a580418690c4d3e1182aa352132d853189410 100644 --- a/src/services/uiService.service.ts +++ b/src/services/uiService.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import {MatSnackBar, MatSnackBarConfig} from "@angular/material/snack-bar"; -import { MatDialog } from "@angular/material/dialog"; +import { MatDialog, MatDialogConfig } from "@angular/material/dialog"; import { ActionDialog } from "src/ui/actionDialog/actionDialog.component"; @Injectable({ @@ -18,7 +18,7 @@ export class UIService { return this.snackbar.open(message, actionBtnTxt, config) } - public showDialog(data, options){ + public showDialog(data: unknown, options: MatDialogConfig){ return this.dialog.open(ActionDialog, { ...options, data diff --git a/src/share/saneUrl/saneUrl.component.spec.ts b/src/share/saneUrl/saneUrl.component.spec.ts index 982ecb76fb47ca954a5da6c9f5c792c69f3118ba..f8a8d3f8f35b5855f5671dbc21ad16c9d8859f02 100644 --- a/src/share/saneUrl/saneUrl.component.spec.ts +++ b/src/share/saneUrl/saneUrl.component.spec.ts @@ -1,13 +1,14 @@ import { TestBed, fakeAsync, tick, flush, ComponentFixture } from '@angular/core/testing' import { SaneUrl } from './saneUrl.component' import { By } from '@angular/platform-browser' -import { BACKENDURL } from 'src/util/constants' import { NoopAnimationsModule } from '@angular/platform-browser/animations' import { SaneUrlSvc } from './saneUrl.service' import { AngularMaterialModule } from 'src/sharedModules' import { CUSTOM_ELEMENTS_SCHEMA, Directive } from '@angular/core' import { of, throwError } from 'rxjs' import { NotFoundError } from '../type' +import { ReactiveFormsModule } from '@angular/forms' +import { CommonModule } from '@angular/common' const inputCss = `input[aria-label="Custom link"]` const submitCss = `button[aria-label="Create custom link"]` @@ -34,6 +35,7 @@ describe('> saneUrl.component.ts', () => { imports: [ NoopAnimationsModule, AngularMaterialModule, + ReactiveFormsModule, ], providers: [ { diff --git a/src/share/saneUrl/saneUrl.component.ts b/src/share/saneUrl/saneUrl.component.ts index f7fbed0ea2bb72dcb5500bcf3d2cec5623b51872..0ce99cce3b2ac0d5167380d9f4182455f2de36e5 100644 --- a/src/share/saneUrl/saneUrl.component.ts +++ b/src/share/saneUrl/saneUrl.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, Input } from "@angular/core"; import { Observable, merge, of, Subscription, BehaviorSubject, combineLatest } from "rxjs"; -import { startWith, mapTo, map, debounceTime, switchMap, catchError, shareReplay, filter, tap, takeUntil, distinctUntilChanged } from "rxjs/operators"; +import { startWith, mapTo, map, debounceTime, switchMap, catchError, shareReplay, filter, tap, distinctUntilChanged } from "rxjs/operators"; import { UntypedFormControl } from "@angular/forms"; import { ErrorStateMatcher } from "@angular/material/core"; import { Clipboard } from "@angular/cdk/clipboard"; @@ -88,7 +88,7 @@ export class SaneUrl implements OnDestroy{ ? of(false) : this.svc.getKeyVal(val).pipe( mapTo(false), - catchError((err, obs) => { + catchError((err) => { if (err instanceof NotFoundError) return of(true) return of(false) }) @@ -164,9 +164,7 @@ export class SaneUrl implements OnDestroy{ this.customUrl.value, this.stateTobeSaved ).subscribe( - resp => { - this.savingProgress$.next(ESavingProgress.DONE) - }, + () => this.savingProgress$.next(ESavingProgress.DONE), err => { this.customUrl.enable() diff --git a/src/share/saneUrl/saneUrl.service.ts b/src/share/saneUrl/saneUrl.service.ts index d78a8ac903089062ba3cc86ca009e627947f70c6..d838c72b83c63d084102e9327b93462821caa703 100644 --- a/src/share/saneUrl/saneUrl.service.ts +++ b/src/share/saneUrl/saneUrl.service.ts @@ -25,7 +25,7 @@ export class SaneUrlSvc implements IKeyValStore{ `${this.saneUrlRoot}${key}`, { responseType: 'json' } ).pipe( - catchError((err, obs) => { + catchError((err) => { const { status } = err if (status === 404) { return throwError(new NotFoundError('Not found')) diff --git a/src/state/index.ts b/src/state/index.ts index 12adc4b974fb893cef495e9310a5b990966478d4..aa9964ec9c0a69f063874b6a8b65382a72e2025c 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -22,6 +22,7 @@ export { export * as generalActions from "./actions" +// eslint-disable-next-line @typescript-eslint/no-unused-vars function debug(reducer: ActionReducer<MainState>): ActionReducer<MainState> { return function(state, action) { console.log('state', state); diff --git a/src/ui/actionDialog/actionDialog.component.ts b/src/ui/actionDialog/actionDialog.component.ts index 0275f8cf576cf236cfe6878446347144412711c4..ced5d32d45c3de46755b1ff7af1f14b8331965b7 100644 --- a/src/ui/actionDialog/actionDialog.component.ts +++ b/src/ui/actionDialog/actionDialog.component.ts @@ -22,7 +22,7 @@ export class ActionDialog{ constructor( @Optional() @Inject(MAT_DIALOG_DATA) data: any ){ - const { config, content, template, actions = [] } = data || {} + const { config, content, actions = [] } = data || {} const { sameLine = false } = config || {} this.content = content @@ -30,7 +30,4 @@ export class ActionDialog{ this.actions = actions } - actionHandler(event: MouseEvent, action: IDialogAction){ - // TODO fill in the actionHandler - } -} \ No newline at end of file +} diff --git a/src/ui/actionDialog/actionDialog.template.html b/src/ui/actionDialog/actionDialog.template.html index 095b27ee6c147c69f53ac164bb7c3f865fd2a021..5388f69f0d10a0d887a2dce39164a273f1e200d5 100644 --- a/src/ui/actionDialog/actionDialog.template.html +++ b/src/ui/actionDialog/actionDialog.template.html @@ -28,8 +28,7 @@ <button *ngSwitchCase="'mat-flat-button'" mat-flat-button [color]="action.color || 'default'" - [mat-dialog-close]="action.dismiss && action" - (click)="actionHandler($event, action)"> + [mat-dialog-close]="action.dismiss && action"> <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container> </button> @@ -37,8 +36,7 @@ <button *ngSwitchCase="'mat-raised-button'" mat-raised-button [color]="action.color || 'default'" - [mat-dialog-close]="action.dismiss && action" - (click)="actionHandler($event, action)"> + [mat-dialog-close]="action.dismiss && action"> <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container> </button> @@ -46,8 +44,7 @@ <button *ngSwitchCase="'mat-stroked-button'" mat-stroked-button [color]="action.color || 'default'" - [mat-dialog-close]="action.dismiss && action" - (click)="actionHandler($event, action)"> + [mat-dialog-close]="action.dismiss && action"> <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container> </button> @@ -55,8 +52,7 @@ <button *ngSwitchDefault mat-button [color]="action.color || 'default'" - [mat-dialog-close]="action.dismiss && action" - (click)="actionHandler($event, action)"> + [mat-dialog-close]="action.dismiss && action"> <ng-container *ngTemplateOutlet="textNodeTmpl; context: action"></ng-container> </button> </ng-container> diff --git a/src/ui/config/module.ts b/src/ui/config/module.ts index 55fcded2c2c01aa17ce1bdb3420dc2e6c9634e5e..751aa6f16aca850c8dc9fd3e951d5e1484ba87bb 100644 --- a/src/ui/config/module.ts +++ b/src/ui/config/module.ts @@ -1,7 +1,6 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { LayoutModule } from "src/layouts/layout.module"; -import { PluginModule } from "src/plugin"; import { AngularMaterialModule } from "src/sharedModules"; import { ConfigComponent } from "./configCmp/config.component"; @@ -9,7 +8,6 @@ import { ConfigComponent } from "./configCmp/config.component"; imports: [ CommonModule, AngularMaterialModule, - // PluginModule, LayoutModule, ], declarations: [ diff --git a/src/ui/dialogInfo/const.ts b/src/ui/dialogInfo/const.ts deleted file mode 100644 index 09aa80f6f979e24b8cd407ec54fcbdfa4c8d3154..0000000000000000000000000000000000000000 --- a/src/ui/dialogInfo/const.ts +++ /dev/null @@ -1 +0,0 @@ -import { InjectionToken } from "@angular/core"; diff --git a/src/ui/help/helpOnePager/helpOnePager.component.ts b/src/ui/help/helpOnePager/helpOnePager.component.ts index 570072137ea77c542c3efb56a60a717063cbef64..d1298937d73f2a5483265cc92d043eafb96cc090 100644 --- a/src/ui/help/helpOnePager/helpOnePager.component.ts +++ b/src/ui/help/helpOnePager/helpOnePager.component.ts @@ -1,4 +1,3 @@ -import { MatDialog } from '@angular/material/dialog'; import { Component } from "@angular/core"; import { ARIA_LABELS } from 'common/constants' diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts deleted file mode 100644 index 4948b3a38be2f43149491447ec4c79a33237ae36..0000000000000000000000000000000000000000 --- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.component.ts +++ /dev/null @@ -1,332 +0,0 @@ -import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, Inject, TemplateRef, ViewChildren, QueryList } from "@angular/core"; -import { combineLatest, concat, fromEvent, merge, Observable, of, Subject, BehaviorSubject } from "rxjs"; -import { filter, map, scan, switchMap, takeUntil, startWith, pairwise, tap, shareReplay, distinctUntilChanged, switchMapTo, reduce } from "rxjs/operators"; -import { clamp } from "src/util/generator"; -import { DOCUMENT } from "@angular/common"; - -const TOUCHMOVE_THRESHOLD = 50 -const SUBMENU_IXOBS_CONFIG = { - -} - -export interface ITunableProp{ - name: string - displayName?: string - values: string[] - selected?: any -} - -@Component({ - selector : 'mobile-overlay', - templateUrl : './mobileOverlay.template.html', - styleUrls : [ - './mobileOverlay.style.css', - ], - styles : [ - ` -div.active > span:before -{ - content: '\u2022'; - width: 1em; - display: inline-block; - background:none; -} -div:not(.active) > span:before -{ - content : ' '; - width : 1em; - display: inline-block; -} - `, - ], -}) - -export class MobileOverlay implements OnInit, OnDestroy { - - @Input('iav-mobile-overlay-guide-tmpl') - public guideTmpl: TemplateRef<any> - - @Input('iav-mobile-overlay-hide-ctrl-btn') - public hideCtrlBtn: boolean = false - - @Input('iav-mobile-overlay-ctrl-btn-pos') - public ctrlBtnPosition: { left: string, top: string } = { left: '50%', top: '50%' } - - @Input() public tunableProperties: ITunableProp[] = [] - - @Output() public tunablePropertySelected: EventEmitter<ITunableProp> = new EventEmitter() - @Output() public deltaValue: EventEmitter<{delta: number, selectedProp: ITunableProp}> = new EventEmitter() - @Output() public valueSelected: EventEmitter<{ value: string, selectedProp: ITunableProp }> = new EventEmitter() - - @ViewChild('initiator', {read: ElementRef, static: true}) public initiator: ElementRef - @ViewChild('mobileMenuContainer', {read: ElementRef, static: true}) public menuContainer: ElementRef - @ViewChild('intersector', {read: ElementRef, static: true}) public intersector: ElementRef - @ViewChild('subMenuObserver', {read: ElementRef, static: false}) public subMenuIx: ElementRef - @ViewChild('setValueContainer', { read: ElementRef, static: false }) public setValueContainer?: ElementRef - - private _onDestroySubject: Subject<boolean> = new Subject() - - private _focusedProperties: ITunableProp - get focusedProperty() { - return this._focusedProperties - ? this._focusedProperties - : this.tunableProperties[0] - } - get focusedIndex() { - return this._focusedProperties - ? this.tunableProperties.findIndex(p => p === this._focusedProperties) - : 0 - } - - private initiatorSingleTouchStart$: Observable<any> - - public showScreen$: Observable<boolean> - public showProperties$: Observable<boolean> - public showDelta$: Observable<boolean> - public showInitiator$: Observable<boolean> - private _drag$: Observable<any> - private _thresholdDrag$: Observable<any> - private intersectionObserver: IntersectionObserver - private subMenuIxObs: IntersectionObserver - - constructor( - @Inject(DOCUMENT) private document: Document - ){ - - } - - public ngOnDestroy() { - this._onDestroySubject.next(true) - this._onDestroySubject.complete() - this.intersectionObserver && this.intersectionObserver.disconnect() - this.subMenuIxObs && this.subMenuIxObs.disconnect() - } - - public ngOnInit() { - - this.initiatorSingleTouchStart$ = fromEvent(this.initiator.nativeElement, 'touchstart').pipe( - filter((ev: TouchEvent) => ev.touches.length === 1), - shareReplay(1) - ) - - const itemCount = this.tunableProperties.length - - const config = { - root: this.intersector.nativeElement, - threshold: [...[...Array(itemCount)].map((_, k) => k / itemCount), 1], - } - - this.intersectionObserver = new IntersectionObserver((arg) => { - if (arg[0].isIntersecting) { - const ratio = arg[0].intersectionRatio - (1 / this.tunableProperties.length / 2) - this.focusItemIndex = this.tunableProperties.length - Math.round(ratio * this.tunableProperties.length) - 1 - } - }, config) - - this.intersectionObserver.observe(this.menuContainer.nativeElement) - - this._drag$ = fromEvent(this.document, 'touchmove').pipe( - filter((ev: TouchEvent) => ev.touches.length === 1), - takeUntil(fromEvent(this.document, 'touchend').pipe( - filter((ev: TouchEvent) => ev.touches.length === 0), - )), - ) - - this._thresholdDrag$ = this._drag$.pipe( - distinctUntilChanged((o, n) => { - const deltaX = o.touches[0].screenX - n.touches[0].screenX - const deltaY = o.touches[0].screenY - n.touches[0].screenY - return (deltaX ** 2 + deltaY ** 2) < TOUCHMOVE_THRESHOLD - }), - ) - - this.showProperties$ = this.initiatorSingleTouchStart$.pipe( - switchMap(() => concat( - this._thresholdDrag$.pipe( - pairwise(), - map(double => ({ - deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX, - deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY, - })), - map(v => v.deltaY ** 2 > v.deltaX ** 2), - scan((acc, _curr) => acc), - ), - of(false) - )), - startWith(false), - distinctUntilChanged(), - shareReplay(1) - ) - - this.showDelta$ = this.initiatorSingleTouchStart$.pipe( - switchMap(() => concat( - this._thresholdDrag$.pipe( - pairwise(), - map(double => ({ - deltaX : double[1].touches[0].screenX - double[0].touches[0].screenX, - deltaY : double[1].touches[0].screenY - double[0].touches[0].screenY, - })), - scan((acc, _curr) => acc), - map(v => v.deltaX ** 2 > v.deltaY ** 2), - ), - of(false), - )), - startWith(false), - distinctUntilChanged(), - shareReplay(1) - ) - - this.showInitiator$ = combineLatest( - this.showProperties$, - this.showDelta$, - ).pipe( - map(([flag1, flag2]) => !flag1 && !flag2), - ) - - this.showScreen$ = combineLatest( - merge( - fromEvent(this.initiator.nativeElement, 'touchstart'), - fromEvent(this.initiator.nativeElement, 'touchend'), - ), - this.showInitiator$, - ).pipe( - map(([ev, showInitiator]: [TouchEvent, boolean]) => showInitiator && ev.touches.length === 1), - ) - - this.showDelta$.pipe( - filter(flag => flag), - switchMapTo(this._thresholdDrag$.pipe( - pairwise(), - map(double => double[1].touches[0].screenX - double[0].touches[0].screenX) - )), - takeUntil(this._onDestroySubject) - ).subscribe(ev => { - this.deltaValue.emit({ - delta: ev, - selectedProp: this.focusedProperty - }) - }) - - const offsetObs$ = this.initiatorSingleTouchStart$.pipe( - switchMap(() => this._drag$) - ) - - combineLatest( - this.showProperties$, - offsetObs$, - ).pipe( - filter(v => v[0]), - map(v => v[1]), - scan((acc, curr) => { - const { startY } = acc - const { screenY } = curr.touches[0] - return { - startY: startY || screenY, - totalDeltaY: screenY - (startY || 0) - } - }, { - startY: null, - totalDeltaY: 0 - }), - takeUntil(this._onDestroySubject), - ).subscribe(v => { - const deltaY = v.totalDeltaY - const cellHeight = this.menuContainer && this.tunableProperties && this.tunableProperties.length > 0 && this.menuContainer.nativeElement.offsetHeight / this.tunableProperties.length - const adjHeight = - this.focusedIndex * cellHeight - cellHeight * 0.5 - - const min = - cellHeight * 0.5 - const max = - this.tunableProperties.length * cellHeight + cellHeight * 0.5 - const finalYTranslate = clamp(adjHeight + deltaY, min, max ) - this.menuTransform = `translate(0px, ${finalYTranslate}px)` - }) - - this.showProperties$.pipe( - takeUntil(this._onDestroySubject), - filter(v => !v), - ).subscribe(() => { - if (this.focusItemIndex >= 0) { - this._focusedProperties = this.tunableProperties[this.focusItemIndex] - } - }) - - this.showDelta$.pipe( - tap(flag => { - this.highlightedSubmenu$.next(this.focusedProperty.values[0]) - if (!flag && !!this.subMenuIxObs) { - this.subMenuIxObs.disconnect() - this.subMenuIxObs = null - } - }), - filter(v => !!v), - // when options show again, options may have changed, so need to recalculate - tap(() => { - this.setValueContainerClampStart = null - this.setValueContainerWidth = null - this.setValueContainerClampEnd = null - this.setValueContainerOffset = null - }), - switchMapTo(this._drag$.pipe( - scan((acc, curr) => { - const { startX } = acc - const { screenX } = curr.touches[0] - return { - startX: startX || screenX, - totalDeltaX: screenX - (startX || 0) - } - }, { - startX: null, - totalDeltaX: 0 - }) - )), - takeUntil(this._onDestroySubject) - ).subscribe(({ totalDeltaX }) => { - if (!this.subMenuIxObs && this.subMenuIx) { - this.subMenuIxObs = new IntersectionObserver(ixs => { - const ix = ixs.find(({ intersectionRatio }) => intersectionRatio < 0.7) - if (!ix) return console.log(ixs) - const value = ix.target.getAttribute('data-submenu-value') - this.highlightedSubmenu$.next(value) - }, { - root: this.subMenuIx.nativeElement, - threshold: [ 0.1, 0.3, 0.5, 0.7, 0.9 ] - }) - - for (const btn of this.setValueContainer.nativeElement.children) { - this.subMenuIxObs.observe(btn) - } - } - if (!this.setValueContainerWidth) { - if (!this.setValueContainer) return - if (this.setValueContainer.nativeElement.children.length === 0) return - const { children, clientWidth } = this.setValueContainer.nativeElement - - this.setValueContainerWidth = clientWidth - const firstChildWidth = children[0].clientWidth - const lastChildWidth = children[children.length - 1].clientWidth - - this.setValueContainerOffset = firstChildWidth / -2 - this.setValueContainerClampStart = firstChildWidth / -2 - this.setValueContainerClampEnd = lastChildWidth / 2 - clientWidth - } - const actualDeltaX = clamp(totalDeltaX + this.setValueContainerOffset, this.setValueContainerClampStart, this.setValueContainerClampEnd) - this.subMenuTransform = `translate(${actualDeltaX}px , 0px)` - }) - - this.showDelta$.pipe( - takeUntil(this._onDestroySubject), - filter(v => !v) - ).subscribe(() => this.valueSelected.emit({ selectedProp: this.focusedProperty, value: this.highlightedSubmenu$.value })) - } - - public highlightedSubmenu$: BehaviorSubject<string> = new BehaviorSubject(null) - - private setValueContainerOffset = null - private setValueContainerClampEnd = null - private setValueContainerClampStart = null - private setValueContainerWidth = null - public subMenuTransform = `translate(0px, 0px)` - public menuTransform = `translate(0px, 0px)` - - public focusItemIndex: number = 0 - -} diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css deleted file mode 100644 index 8d548523eaefd725bb5baa490dfc38e3e7abdc2e..0000000000000000000000000000000000000000 --- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css +++ /dev/null @@ -1,110 +0,0 @@ -:host -{ - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - z-index: 9; - - pointer-events: none; -} - -:host [screen] -{ - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - z-index: 99999; - - color : black; - background-color: rgba(255, 255, 255, 0.5); - - transition: all 200ms linear; -} - -:host-context([darktheme="true"]) [screen] -{ - color : white; - background-color: rgba(0, 0, 0, 0.5); -} - -[intersector] -{ - position: absolute; - top: 50%; - left: 0; - width: 100%; - height: 50%; - - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: center; -} - -[mobileMenuContainer] -{ - z-index: 1000; -} - -.scrollFocus:after -{ - content: ' '; - position:absolute; - width:100%; - height:100%; - top: 0; - left: 0; - - background-color: rgba(0, 0, 128, 0.2); -} - -:host-context([darktheme="true"]) .scrollFocus:after -{ - background-color: rgba(128, 128, 200, 0.2); -} - -.base-container -{ - position: relative; - width: 100%; - left: 0; - top: 50%; - height: 0; - z-index: 9999; -} - -div[delta] -{ - white-space: nowrap -} - -.popup -{ - transition: all 120ms linear; - transform-origin: 50% 100%; -} - -.scale-y-0 -{ - transform: scale(0.5, 0); - opacity: 0; -} - -.subMenu -{ - bottom: 15px; -} - -.w-50 -{ - width: 50%; -} - -.sliver -{ - width: 1px; -} \ No newline at end of file diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.template.html b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.template.html deleted file mode 100644 index 791edddf1b9dda7ae39043f7746afa8703f87bef..0000000000000000000000000000000000000000 --- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.template.html +++ /dev/null @@ -1,82 +0,0 @@ -<div screen [hidden]="!(showProperties$ | async)"> - <div intersector #intersector> - <div [style.transform] = "menuTransform" class = "btn-group-vertical" role = "group" mobileMenuContainer #mobileMenuContainer> - <div - *ngFor = "let p of tunableProperties; let i = index" - [ngClass] = "{'active' : p === focusedProperty, 'scrollFocus': i === focusItemIndex}" - class = "btn btn-default theme-controlled property scrollFocus"> - <!-- scrollFocus class --> - <span> - {{ p.displayName || p.name }} - </span> - </div> - </div> - </div> -</div> - -<!-- container class --> -<div class="d-flex flex-column-reverse flex-nowrap align-items-center base-container position-relative"> - - <!-- ctrl nub --> - <div class="h-0 d-inline-flex align-items-center" [hidden]="!(showInitiator$ | async)" #initiator> - <div (contextmenu)="$event.stopPropagation(); $event.preventDefault();" - [ngStyle]="ctrlBtnPosition" - class="pe-all" - initiator> - <button mat-mini-fab color="primary"> - <i class="fas fa-globe"></i> - </button> - </div> - </div> - - <!-- guide text --> - <mat-card [ngClass]="{ 'scale-y-0': !(showScreen$ | async) }" - class="mb-4 popup muted position-absolute subMenu"> - <mat-card-content> - <ng-container *ngTemplateOutlet="guideTmpl"> - </ng-container> - </mat-card-content> - </mat-card> - - <!-- mobile set value --> - <div *ngIf="showDelta$ | async" class="position-absolute h-0 w-100 d-flex flex-row justify-content-end align-items-end"> - - <!-- intersection observer --> - <div class="w-50 d-flex flex-row flex-nowrap" #subMenuObserver> - - <!-- value selection container --> - <div class="position-relative mb-4" [style.transform]="subMenuTransform" #setValueContainer> - <!-- value selections --> - <ng-container *ngFor="let value of focusedProperty.values"> - <!-- selected button --> - <ng-template - [ngIf]="focusedProperty.selected === value" - [ngIfElse]="notSelectedTmpl"> - <button - [attr.data-submenu-value]="value" - mat-flat-button - [ngClass]="{ 'muted': (highlightedSubmenu$ | async) !== value }" - color="primary" - class="mr-2"> - {{ value }} - </button> - </ng-template> - - <!-- not selected button --> - <ng-template #notSelectedTmpl> - <button - [attr.data-submenu-value]="value" - mat-flat-button - [ngClass]="{ 'muted': (highlightedSubmenu$ | async) !== value }" - color="default" - class="mr-2"> - {{ value }} - </button> - </ng-template> - - </ng-container> - </div> - </div> - </div> - -</div> diff --git a/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts b/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts deleted file mode 100644 index 3e76f4d0234a6942743f9eeff983eb065b938298..0000000000000000000000000000000000000000 --- a/src/ui/nehubaContainer/reorderPanelIndex.pipe.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core"; - -@Pipe({ - name: 'reorderPanelIndexPipe', -}) - -export class ReorderPanelIndexPipe implements PipeTransform { - public transform(panelOrder: string, uncorrectedIndex: number) { - return uncorrectedIndex === null - ? null - : panelOrder.indexOf(uncorrectedIndex.toString()) - } -} diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 1d9c79d562f70331b06637468450d964b6d733b4..2e083c87bbc17a268a43c2aba3cf1dd90acc5018 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -6,10 +6,6 @@ import { ScrollingModule } from "@angular/cdk/scrolling" import { HttpClientModule } from "@angular/common/http"; import { AngularMaterialModule } from 'src/sharedModules' import { UtilModule } from "src/util"; -import { DownloadDirective } from "../util/directives/download.directive"; -import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component"; -import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe"; -import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe"; import { ShareModule } from "src/share"; import { AuthModule } from "src/auth"; import { ActionDialog } from "./actionDialog/actionDialog.component"; @@ -33,13 +29,7 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens AuthModule, ], declarations: [ - MobileOverlay, ActionDialog, - /* pipes */ - HumanReadableFileSizePipe, - ReorderPanelIndexPipe, - /* directive */ - DownloadDirective, ], providers: [ { @@ -51,8 +41,10 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens provide: HANDLE_SCREENSHOT_PROMISE, useValue: ((param) => { const canvas: HTMLCanvasElement = document.querySelector('#neuroglancer-container canvas') - if (!canvas) return Promise.reject(`element '#neuroglancer-container canvas' not found`) - const _ = (window as any).viewer.display.draw() + if (!canvas) { + return Promise.reject(`element '#neuroglancer-container canvas' not found`) + } + (window as any).viewer.display.draw() if (!param) { return new Promise(rs => { canvas.toBlob(blob => { @@ -99,9 +91,6 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens } ], exports: [ - // NehubaContainer, - MobileOverlay, - // StatusCardComponent, ] }) diff --git a/src/util/directives/download.directive.ts b/src/util/directives/download.directive.ts deleted file mode 100644 index c0851002d0977cd7e534bc0bbf0b99cd446a7921..0000000000000000000000000000000000000000 --- a/src/util/directives/download.directive.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Directive, ElementRef, Renderer2 } from "@angular/core"; - -@Directive({ - selector : 'a[download]', -}) - -export class DownloadDirective { - - public downloadIcon: HTMLElement - - constructor(public el: ElementRef, public rd2: Renderer2) { - this.downloadIcon = rd2.createElement('i') - rd2.addClass(this.downloadIcon, 'fas') - rd2.addClass(this.downloadIcon, 'fa-download-alt') - } - - public ngAfterViewInit() { - this.rd2.appendChild(this.el.nativeElement, this.downloadIcon) - } -} diff --git a/src/util/fn.ts b/src/util/fn.ts index ff9b1c07fc1d93938e4f6e62a990aa34ce84422a..6457ccf78a80061da34734ae0c3b2e20b0ffe238 100644 --- a/src/util/fn.ts +++ b/src/util/fn.ts @@ -17,6 +17,9 @@ export function getDebug() { return (window as any).__DEBUG__ } +// eslint-disable-next-line @typescript-eslint/no-empty-function +export function noop(){} + export async function getExportNehuba() { // eslint-disable-next-line no-constant-condition while (true) { @@ -26,15 +29,6 @@ export async function getExportNehuba() { } } -const recursiveFlatten = (region, {ngId}) => { - return [{ - ngId, - ...region, - }].concat( - ...((region.children && region.children.map && region.children.map(c => recursiveFlatten(c, { ngId : region.ngId || ngId })) ) || []), - ) -} - export function getUuid(){ return crypto.getRandomValues(new Uint32Array(1))[0].toString(16) } @@ -140,7 +134,7 @@ export const CachedFunction = (config?: TCacheFunctionArg) => { } } -// A quick, non security hash function +// A quick, non secure hash function export class QuickHash { private length = 6 constructor(opts?: any){ @@ -423,7 +417,7 @@ export function bufferUntil<T>(opts: ISwitchMapWaitFor) { export function defaultdict<T>(fn: () => T): Record<string, T> { const obj = {} return new Proxy(obj, { - get(target, prop, rec) { + get(target, prop, _rec) { if (!(prop in target)){ target[prop] = fn() } diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index 6c057cb1ebe6acb7381f58864a0511cb3c1dc061..b9ba0df799dcd95099fdc94e718d20342bbfe62e 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -3,7 +3,6 @@ */ import { InjectionToken } from "@angular/core" -import { Observable } from "rxjs" export interface IHasId{ ['@id']: string diff --git a/src/util/pipes/humanReadableFileSize.pipe.spec.ts b/src/util/pipes/humanReadableFileSize.pipe.spec.ts deleted file mode 100644 index 74ae1113992802d8994023e8724acb4ee30a97ca..0000000000000000000000000000000000000000 --- a/src/util/pipes/humanReadableFileSize.pipe.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {} from 'jasmine' -import { HumanReadableFileSizePipe } from './humanReadableFileSize.pipe' - -describe('humanReadableFileSize.pipe.ts', () => { - describe('HumanReadableFileSizePipe', () => { - it('steps properly when nubmers ets large', () => { - const pipe = new HumanReadableFileSizePipe() - const num = 12 - - expect(pipe.transform(num, 0)).toBe(`12 byte(s)`) - expect(pipe.transform(num * 1e3, 0)).toBe(`12 KB`) - expect(pipe.transform(num * 1e6, 0)).toBe(`12 MB`) - expect(pipe.transform(num * 1e9, 0)).toBe(`12 GB`) - expect(pipe.transform(num * 1e12, 0)).toBe(`12 TB`) - - expect(pipe.transform(num * 1e15, 0)).toBe(`12000 TB`) - }) - - it('pads the correct zeros', () => { - const pipe = new HumanReadableFileSizePipe() - const num = 3.14159 - - expect(pipe.transform(num, 0)).toBe(`3 byte(s)`) - expect(pipe.transform(num, 1)).toBe(`3.1 byte(s)`) - expect(pipe.transform(num, 2)).toBe(`3.14 byte(s)`) - expect(pipe.transform(num, 3)).toBe(`3.142 byte(s)`) - expect(pipe.transform(num, 4)).toBe(`3.1416 byte(s)`) - expect(pipe.transform(num, 5)).toBe(`3.14159 byte(s)`) - expect(pipe.transform(num, 6)).toBe(`3.141590 byte(s)`) - expect(pipe.transform(num, 7)).toBe(`3.1415900 byte(s)`) - }) - - it('parses string as well as number', () => { - // TODO finish tests - }) - - it('throws when a non number is passed to either argument', () => { - // TODO finish tests - }) - - }) -}) diff --git a/src/util/pipes/humanReadableFileSize.pipe.ts b/src/util/pipes/humanReadableFileSize.pipe.ts deleted file mode 100644 index 1abb96e6bcb826dd64a4ebda3183a7e3b910df85..0000000000000000000000000000000000000000 --- a/src/util/pipes/humanReadableFileSize.pipe.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core"; - -export const steps = [ - 'byte(s)', - 'KB', - 'MB', - 'GB', - 'TB', -] - -@Pipe({ - name: 'humanReadableFileSizePipe', -}) - -export class HumanReadableFileSizePipe implements PipeTransform { - public transform(input: string | number, precision: number = 2) { - let _input = Number(input) - if (!_input) { throw new Error(`HumanReadableFileSizePipe needs a string or a number that can be parsed to number`) } - const _precision = Number(precision) - if (isNaN(_precision)) { throw new Error(`precision must be a number`) } - let counter = 0 - while (_input > 1000 && counter < 4) { - _input = _input / 1000 - counter += 1 - } - return `${_input.toFixed(precision)} ${steps[counter]}` - } -} diff --git a/src/util/pullable.ts b/src/util/pullable.ts index 24ae06f3b70e51ac84cef085807866dfea5a2ade..6a5d87e96701b3c9c41553ce66c0f611054c0afa 100644 --- a/src/util/pullable.ts +++ b/src/util/pullable.ts @@ -1,5 +1,5 @@ import { DataSource } from "@angular/cdk/collections" -import { BehaviorSubject, Observable, ReplaySubject, Subscription, combineLatest, concat, of, timer } from "rxjs" +import { BehaviorSubject, Observable, ReplaySubject, Subscription, concat, of, timer } from "rxjs" import { finalize, map, scan, shareReplay, startWith, tap } from "rxjs/operators" import { cachedPromise } from "./fn" diff --git a/src/util/side-panel/side-panel.component.spec.ts b/src/util/side-panel/side-panel.component.spec.ts index 73a50a69a04470d96248180aafab1d09ced161fd..5a7f71a4d35e01422c590a717f306dae3780dc0d 100644 --- a/src/util/side-panel/side-panel.component.spec.ts +++ b/src/util/side-panel/side-panel.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SidePanelComponent } from './side-panel.component'; +import { MatCardModule } from '@angular/material/card'; describe('SidePanelComponent', () => { let component: SidePanelComponent; @@ -8,7 +9,12 @@ describe('SidePanelComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ SidePanelComponent ] + imports: [ + MatCardModule, + ], + declarations: [ + SidePanelComponent, + ] }) .compileComponents(); diff --git a/src/viewerModule/nehuba/base.service/base.service.ts b/src/viewerModule/nehuba/base.service/base.service.ts index bb42ed31f608691ce4b91084927b9a44d0a8a558..a6bfe046ab51e81f2d84db4930e9747d22f0388d 100644 --- a/src/viewerModule/nehuba/base.service/base.service.ts +++ b/src/viewerModule/nehuba/base.service/base.service.ts @@ -44,7 +44,7 @@ export class BaseService { regionmap.set(r.name, r) } const returnVal: Record<string, Record<number, NgMapReturnType>> = {} - for (const [url, { layer, region }] of Object.entries(record)) { + for (const [ /* url */ , { layer, region }] of Object.entries(record)) { for (const { name, label } of region) { diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts index 68f181a8654696b4c3c080bc8f079f694dce4591..b8e5e4b26a8f59e6f0dce71eaf8ee227d6e46e80 100644 --- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts +++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts @@ -172,7 +172,7 @@ export class LayerCtrlEffects { parcNgLayers: from(this.sapi.getTranslatedLabelledNgMap(parcellation, template)).pipe( map(val => { const returnVal: Record<string, NgSegLayerSpec> = {} - for (const [ url, { layer, region } ] of Object.entries(val)) { + for (const [ /** url */, { layer, region } ] of Object.entries(val)) { const { name } = region[0] const ngId = getParcNgId(atlas, template, parcellation, { id: '', diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts index a6e58b8462cb398d87f2f6b26b61607110950f2d..118031cc0806bb692dead3bff0f7811adae32759 100644 --- a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts +++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.component.ts @@ -4,7 +4,7 @@ import { combineLatest, fromEvent, interval, merge, Observable, of, Subject, Sub import { userInterface } from "src/state"; import { NehubaViewerUnit } from "../../nehubaViewer/nehubaViewer.component"; import { NEHUBA_INSTANCE_INJTKN, takeOnePipe, getFourPanel, getHorizontalOneThree, getSinglePanel, getPipPanel, getVerticalOneThree } from "../../util"; -import { QUICKTOUR_DESC, ARIA_LABELS, IDS } from 'common/constants' +import { QUICKTOUR_DESC, QUICKTOUR_DESC_MD, ARIA_LABELS, IDS } from 'common/constants' import { IQuickTourData } from "src/ui/quickTour/constrants"; import { debounce, debounceTime, distinctUntilChanged, filter, map, mapTo, switchMap, take } from "rxjs/operators"; import {panelOrder} from "src/state/userInterface/selectors"; @@ -28,6 +28,7 @@ export class NehubaLayoutOverlay implements OnDestroy{ public quickTourSliceViewSlide: IQuickTourData = { order: 1, description: QUICKTOUR_DESC.SLICE_VIEW, + descriptionMd: QUICKTOUR_DESC_MD.SLICE_VIEW } public quickTour3dViewSlide: IQuickTourData = { diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html index 5ae334a2db080f80a983734c1004c3e3e46e231b..18968039f924b3625f13254ff2b2d6e0bf346f61 100644 --- a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html +++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html @@ -7,6 +7,7 @@ [iav-window-resize-time]="64" (iav-window-resize-event)="setQuickTourPos()" quick-tour + [quick-tour-description-md]="quickTourSliceViewSlide.descriptionMd" [quick-tour-description]="quickTourSliceViewSlide.description" [quick-tour-order]="quickTourSliceViewSlide.order" [quick-tour-overwrite-arrow]="sliceViewArrow" diff --git a/src/viewerModule/nehuba/mesh.service/mesh.service.ts b/src/viewerModule/nehuba/mesh.service/mesh.service.ts index d46e128f4a84d082272101b67a215bf75d5165c3..2af102701593d4918f8b4c45e3eef92f2382952b 100644 --- a/src/viewerModule/nehuba/mesh.service/mesh.service.ts +++ b/src/viewerModule/nehuba/mesh.service/mesh.service.ts @@ -39,7 +39,7 @@ export class NehubaMeshService implements OnDestroy { const ngIdRecord: Record<string, number[]> = {} for (const [ngId, labelToRegion] of Object.entries(record)) { - for (const [label, _region] of Object.entries(labelToRegion)) { + for (const [label, ] of Object.entries(labelToRegion)) { if (!ngIdRecord[ngId]) { ngIdRecord[ngId] = [] } diff --git a/src/viewerModule/nehuba/module.ts b/src/viewerModule/nehuba/module.ts index 8243707dae27438a851e0ccd38cf39fa428519ff..682fae0bb9cf878ebd3ff18007a1ad26eb8cbfb0 100644 --- a/src/viewerModule/nehuba/module.ts +++ b/src/viewerModule/nehuba/module.ts @@ -1,4 +1,4 @@ -import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core"; +import { APP_INITIALIZER, NgModule } from "@angular/core"; import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive' import { IMPORT_NEHUBA_INJECT_TOKEN, NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"; import { CommonModule } from "@angular/common"; diff --git a/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts b/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts index dd82454761ad41d4ccb18f8a37ea3abbabe3c6f5..66413f1ffe994dab36f2ecc0aa3eab5c6225b374 100644 --- a/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts +++ b/src/viewerModule/nehuba/navigation.service/navigation.base.service.ts @@ -1,6 +1,6 @@ import { Inject, Injectable, Optional } from "@angular/core"; import { concat, EMPTY, NEVER, Observable, of } from "rxjs"; -import { delay, exhaustMap, shareReplay, switchMap, take, tap } from "rxjs/operators"; +import { delay, exhaustMap, shareReplay, switchMap, take } from "rxjs/operators"; import { TNehubaViewerUnit } from "../constants"; import { NEHUBA_INSTANCE_INJTKN } from "../util"; diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts index e1806ae87856ee7dd6756f15e122ce54e3bc1409..e0e25cd158aedca533bbe1b9a336832ff2f82298 100644 --- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts +++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts @@ -13,17 +13,6 @@ import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl. */ import { INgLayerCtrl, NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY, TNgLayerCtrl } from "../layerCtrl.service/layerCtrl.util"; -const NG_LANDMARK_LAYER_NAME = 'spatial landmark layer' -const NG_USER_LANDMARK_LAYER_NAME = 'user landmark layer' - -/** - * optimized for nehubaConfig.layout.useNehubaPerspective.fixedZoomPerspectiveSlices - * sliceZoom - * sliceViewportWidth - * sliceViewportHeight - */ -const NG_LANDMARK_CONSTANT = 1e-8 - export const IMPORT_NEHUBA_INJECT_TOKEN = `IMPORT_NEHUBA_INJECT_TOKEN` interface LayerLabelIndex { @@ -85,8 +74,7 @@ export class NehubaViewerUnit implements OnDestroy { url?: string } }> = new EventEmitter() - @Output() public mouseoverLandmarkEmitter: EventEmitter<string> = new EventEmitter() - @Output() public mouseoverUserlandmarkEmitter: EventEmitter<string> = new EventEmitter() + @Output() public regionSelectionEmitter: EventEmitter<{ segment: number layer: { @@ -95,6 +83,7 @@ export class NehubaViewerUnit implements OnDestroy { }}> = new EventEmitter() @Output() public errorEmitter: EventEmitter<any> = new EventEmitter() + @Output() public totalMeshesToLoad = new EventEmitter<number>() /* only used to set initial navigation state */ public initNav: any @@ -319,7 +308,7 @@ export class NehubaViewerUnit implements OnDestroy { totalMeshes += labelIndicies.length this.nehubaViewer.setMeshesToLoad(labelIndicies, layer) } - // TODO implement total mesh to be loaded and mesh loading UI + this.totalMeshesToLoad.emit(totalMeshes) }), ) } else { @@ -336,25 +325,6 @@ export class NehubaViewerUnit implements OnDestroy { } } - public spatialLandmarkSelectionChanged(labels: number[]) { - const getCondition = (label: number) => `if(label > ${label - 0.1} && label < ${label + 0.1} ){${FRAGMENT_EMIT_RED}}` - const newShader = `void main(){ ${labels.map(getCondition).join('else ')}else {${FRAGMENT_EMIT_WHITE}} }` - if (!this.nehubaViewer) { - this.log.warn('setting special landmark selection changed failed ... nehubaViewer is not yet defined') - return - } - const landmarkLayer = this.nehubaViewer.ngviewer.layerManager.getLayerByName(NG_LANDMARK_LAYER_NAME) - if (!landmarkLayer) { - this.log.warn('landmark layer could not be found ... will not update colour map') - return - } - if (labels.length === 0) { - landmarkLayer.layer.displayState.fragmentMain.restoreState(FRAGMENT_MAIN_WHITE) - } else { - landmarkLayer.layer.displayState.fragmentMain.restoreState(newShader) - } - } - public navPosReal: [number, number, number] = [0, 0, 0] public navPosVoxel: [number, number, number] = [0, 0, 0] @@ -483,20 +453,6 @@ export class NehubaViewerUnit implements OnDestroy { ) } - private userLandmarkShader: string = FRAGMENT_MAIN_WHITE - - public removeSpatialSearch3DLandmarks() { - this.removeLayer({ - name : NG_LANDMARK_LAYER_NAME, - }) - } - - public removeuserLandmarks() { - this.removeLayer({ - name : NG_USER_LANDMARK_LAYER_NAME, - }) - } - public setLayerVisibility(condition: {name: string|RegExp}, visible: boolean) { if (!this.nehubaViewer) { return false @@ -798,19 +754,6 @@ export class NehubaViewerUnit implements OnDestroy { }) }) - // TODO bug: mouseoverlandmarkemitter does not emit empty for VTK layer when user mouse click - this.ondestroySubscriptions.push( - this.nehubaViewer.mouseOver.layer - .filter(obj => obj.layer.name === NG_LANDMARK_LAYER_NAME) - .subscribe(obj => this.mouseoverLandmarkEmitter.emit(obj.value)), - ) - - this.ondestroySubscriptions.push( - this.nehubaViewer.mouseOver.layer - .filter(obj => obj.layer.name === NG_USER_LANDMARK_LAYER_NAME) - .subscribe(obj => this.mouseoverUserlandmarkEmitter.emit(obj.value)), - ) - this._s4$ = this.nehubaViewer.navigationState.position.inRealSpace .filter(v => typeof v !== 'undefined' && v !== null) .subscribe(v => { diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts index 1ff596295312d22d8d87e775c3c12821f0f1cbcb..c3bc899bd568f5242f37f4a90f2cf1f57f78579b 100644 --- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts +++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output, TemplateRef, ViewChild } from "@angular/core"; +import { ChangeDetectionStrategy, Component, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util"; import { distinctUntilChanged } from "rxjs/operators"; diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts index 19314d6737ebfd0c3229b41f3fb62055d9b0bcb6..db6f3a030a26adaaf5748a2a0e1b4816f0080c85 100644 --- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts +++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.spec.ts @@ -94,8 +94,6 @@ describe('> nehubaViewerInterface.directive.ts', () => { layersChanged: new Subject(), nehubaReady: new Subject(), mouseoverSegmentEmitter: new Subject(), - mouseoverLandmarkEmitter: new Subject(), - mouseoverUserlandmarkEmitter: new Subject(), elementRef: { nativeElement: {} }, @@ -171,76 +169,7 @@ describe('> nehubaViewerInterface.directive.ts', () => { }) describe('> subscription of nehuba instance', () => { - describe('> mouseoverUserlandmarkEmitter', () => { - let spyNehubaViewerInstance: any - let dispatchSpy: jasmine.Spy - let directiveInstance: NehubaViewerContainerDirective - const template = {} - const lifecycle = {} - beforeEach(() => { - - spyNehubaViewerInstance = { - config: null, - lifecycle: null, - templateId: null, - errorEmitter: new Subject(), - viewerPositionChange: new Subject(), - layersChanged: new Subject(), - nehubaReady: new Subject(), - mouseoverSegmentEmitter: new Subject(), - mouseoverLandmarkEmitter: new Subject(), - mouseoverUserlandmarkEmitter: new Subject(), - elementRef: { - nativeElement: {} - } - } - const mockStore = TestBed.inject(MockStore) - dispatchSpy = spyOn(mockStore, 'dispatch') - - const fixture = TestBed.createComponent(DummyCmp) - const directive = fixture.debugElement.query( - By.directive(NehubaViewerContainerDirective) - ) - - const spyComRef = { - destroy: jasmine.createSpy('destroy') - } - directiveInstance = directive.injector.get(NehubaViewerContainerDirective) - spyOnProperty(directiveInstance, 'nehubaViewerInstance').and.returnValue(spyNehubaViewerInstance) - spyOn(directiveInstance['el'], 'clear').and.callFake(() => {}) - spyOn(directiveInstance, 'clear').and.callFake(() => {}) - // casting return value to any is not perfect, but since only 2 methods and 1 property is used, it's a quick way - // rather than allow component to be created - spyOn(directiveInstance['el'], 'createComponent').and.returnValue(spyComRef as any) - - }) - - afterEach(() => { - dispatchSpy.calls.reset() - }) - it('> single null emits null', fakeAsync(() => { - - })) - it('> single value emits value', fakeAsync(() => { - - })) - - describe('> double value in 140ms emits last value', () => { - - it('> null - 24 emits 24', fakeAsync(() => { - - })) - it('> 24 - null emits null', fakeAsync(() => { - - - })) - }) - - it('> single value outside 140 ms emits separately', fakeAsync(() => { - - })) - }) }) }) }) diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts index aee39e6f48641ebdb9b5b8051f23c6c023a91545..45d9228cb40e036d94b3d78c644c722cf043e600 100644 --- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts +++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts @@ -1,8 +1,8 @@ -import { Directive, ViewContainerRef, ComponentRef, OnDestroy, Output, EventEmitter, Optional, ChangeDetectorRef, ComponentFactoryResolver, ComponentFactory } from "@angular/core"; +import { Directive, ViewContainerRef, ComponentRef, OnDestroy, Output, EventEmitter, Optional, ChangeDetectorRef } from "@angular/core"; import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component"; import { Store, select } from "@ngrx/store"; -import { Subscription, Observable, asyncScheduler, combineLatest } from "rxjs"; -import { distinctUntilChanged, filter, debounceTime, scan, map, throttleTime, switchMap, take, tap } from "rxjs/operators"; +import { Subscription, Observable, combineLatest } from "rxjs"; +import { distinctUntilChanged, filter, debounceTime, scan, map, switchMap, take } from "rxjs/operators"; import { serializeSegment } from "../util"; import { LoggingService } from "src/logging"; import { arrayOfPrimitiveEqual } from 'src/util/fn' @@ -313,19 +313,6 @@ export class NehubaViewerContainerDirective implements OnDestroy{ map((map: Map<string, any>) => Array.from(map.entries()).filter(([_ngId, { segmentId }]) => segmentId)), ).subscribe(val => this.handleMouseoverSegments(val)), - this.nehubaViewerInstance.mouseoverLandmarkEmitter.pipe( - distinctUntilChanged() - ).subscribe(label => { - console.warn(`mouseover landmark`, label) - }), - - this.nehubaViewerInstance.mouseoverUserlandmarkEmitter.pipe( - throttleTime(160, asyncScheduler, {trailing: true}), - ).subscribe(label => { - const idx = Number(label.replace('label=', '')) - // TODO - // this is exclusive for vtk layer - }), combineLatest([ this.nehubaViewerInstance.mousePosInVoxel$, diff --git a/src/viewerModule/nehuba/util.ts b/src/viewerModule/nehuba/util.ts index 81aed3560abe6fe1f41b9faf13211d1002702591..c8d56c7e5ba037ab8366e4ca40d0066481146789 100644 --- a/src/viewerModule/nehuba/util.ts +++ b/src/viewerModule/nehuba/util.ts @@ -1,7 +1,7 @@ import { InjectionToken } from '@angular/core' import { Observable, pipe } from 'rxjs' import { filter, scan, take } from 'rxjs/operators' -import { getExportNehuba, getViewer } from 'src/util/fn' +import { getViewer } from 'src/util/fn' import { NehubaViewerUnit } from './nehubaViewer/nehubaViewer.component' import { userInterface } from 'src/state' diff --git a/src/viewerModule/nehuba/viewerCtrl/effects.ts b/src/viewerModule/nehuba/viewerCtrl/effects.ts index fd10b9db4dabeadb61bd8f75683b3492081d7a74..5eeaafed8eaf61c7d03a5ddee0cb55759b39a8e6 100644 --- a/src/viewerModule/nehuba/viewerCtrl/effects.ts +++ b/src/viewerModule/nehuba/viewerCtrl/effects.ts @@ -2,7 +2,7 @@ import { Injectable } from "@angular/core"; import { createEffect } from "@ngrx/effects"; import { Store } from "@ngrx/store"; import { of } from "rxjs"; -import { mapTo, switchMap } from "rxjs/operators"; +import { switchMap } from "rxjs/operators"; import { atlasSelection, userInterface } from "src/state"; @Injectable() diff --git a/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts b/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts index 67706f44a39f0b2600e75574dcfe8e25f41a18ac..6342411ebf03fabb2d1c816eac98cf15695a8451 100644 --- a/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts +++ b/src/viewerModule/nehuba/viewerCtrl/perspectiveViewSlider/perspectiveViewSlider.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, Inject, ViewChild, ChangeDetectionStrategy } from "@angular/core"; import { FormControl } from "@angular/forms"; import { select, Store } from "@ngrx/store"; -import { combineLatest, concat, forkJoin, NEVER, Observable, of, Subject, Subscription, throwError } from "rxjs"; +import { combineLatest, concat, NEVER, Observable, of, Subject, Subscription } from "rxjs"; import { switchMap, distinctUntilChanged, map, debounceTime, shareReplay, take, withLatestFrom } from "rxjs/operators"; import { SAPI } from "src/atlasComponents/sapi"; import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes" @@ -292,7 +292,7 @@ export class PerspectiveViewSlider implements OnDestroy { this.currentTemplateSize$, this.rangeControlIsVertical$, ]).pipe( - map(([ navigation, viewportSize, ctrl, templateSize, isVertical ]) => { + map(([ navigation, viewportSize, ctrl, templateSize, ..._rest ]) => { if (!ctrl || !(templateSize?.real) || !navigation) return null const { zoom, position } = navigation diff --git a/src/viewerModule/threeSurfer/store/effects.ts b/src/viewerModule/threeSurfer/store/effects.ts index 501107552338fc19658b5757c13610b3fae15782..86992a1c3330ed1b3150467d04aa4f984cde169e 100644 --- a/src/viewerModule/threeSurfer/store/effects.ts +++ b/src/viewerModule/threeSurfer/store/effects.ts @@ -1,7 +1,7 @@ import { Injectable } from "@angular/core"; import { createEffect } from "@ngrx/effects"; import { select, Store } from "@ngrx/store"; -import { EMPTY, forkJoin, merge, Observable, of, pipe, throwError } from "rxjs"; +import { EMPTY, forkJoin, merge, Observable, of, pipe } from "rxjs"; import { debounceTime, map, switchMap, withLatestFrom, filter, shareReplay, distinctUntilChanged } from "rxjs/operators"; import { SAPI } from "src/atlasComponents/sapi"; import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes" @@ -118,7 +118,7 @@ export class ThreeSurferEffects { )) onATPDebounceAddBaseLayers$ = createEffect(() => this.onATPDebounceThreeSurferLayers$.pipe( - switchMap(({ labels, surfaces }) => { + switchMap(({ labels }) => { const labelMaps: ThreeSurferCustomLabelLayer[] = [] for (const key in labels) { const { laterality, url } = labels[key] diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts index e4e729b1cfcf6817d00afd57681e2bbc9c817e9c..60ba8899d52403374120fdcd20c5460a3afd1734 100644 --- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts +++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts @@ -613,7 +613,7 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit this.tsRef.applyColorMap(mesh, vertexIndices, { custom: actualApplyMap }) - return + continue } const highlightIdx = new Set<number>() diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts index 2dbd534dbd0a309ec23fcf1f6545a88bfe1b5a18..b0d53ca1162ec2a39f94a90a091c32dbd5ea3663 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.component.ts +++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core"; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { combineLatest, Observable, Subscription } from "rxjs"; import { debounceTime, map, shareReplay } from "rxjs/operators"; @@ -444,8 +444,8 @@ export class ViewerCmp implements OnDestroy { voiFeatureEntryCmp: EntryComponent async pullAllVoi(){ + await wait(320) if (this.voiFeatureEntryCmp){ - await wait(320) this.voiFeatureEntryCmp.pullAll() } } diff --git a/src/widget/service.ts b/src/widget/service.ts index 9430269418731ceb3b57810b54b26ef155ef7ef0..67856fb1f1c79ed1de0145394e3148f99c7bb633 100644 --- a/src/widget/service.ts +++ b/src/widget/service.ts @@ -1,5 +1,5 @@ import { ComponentPortal } from "@angular/cdk/portal"; -import { ComponentFactory, ComponentFactoryResolver, ComponentRef, Injectable, Injector, ViewContainerRef } from "@angular/core"; +import { ComponentRef, Injectable, Injector, ViewContainerRef } from "@angular/core"; import { RM_WIDGET } from "./constants"; import { WidgetPortal } from "./widgetPortal/widgetPortal.component"; @@ -12,11 +12,6 @@ export class WidgetService { public vcr: ViewContainerRef private viewRefMap = new Map<WidgetPortal<unknown>, ComponentRef<WidgetPortal<unknown>>>() - private cf: ComponentFactory<WidgetPortal<unknown>> - - constructor(cfr: ComponentFactoryResolver){ - this.cf = cfr.resolveComponentFactory(WidgetPortal) - } public addNewWidget<T>(Component: new (...arg: any) => T, injector: Injector): WidgetPortal<T> { const inj = Injector.create({ @@ -26,7 +21,8 @@ export class WidgetService { }], parent: injector }) - const widgetPortal = this.vcr.createComponent(this.cf, 0, inj) as ComponentRef<WidgetPortal<T>> + + const widgetPortal = this.vcr.createComponent(WidgetPortal, {index: 0, injector: inj}) as ComponentRef<WidgetPortal<T>> const cmpPortal = new ComponentPortal<T>(Component, this.vcr, inj) this.viewRefMap.set(widgetPortal.instance, widgetPortal)