From b785d802a1771651081985939a8664101ac5bd04 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Fri, 25 Jun 2021 19:50:02 +0200 Subject: [PATCH] chore: update endpoint, feat: loading indicator for ebrains features --- Dockerfile | 2 +- deploy/datasets/index.js | 1 + .../parcellationRegion/module.ts | 6 + .../regionAccordionTooltipText.pipe.ts | 4 - .../regionMenu/regionMenu.component.ts | 54 +++- .../regionMenu/regionMenu.template.html | 236 +++++++++++------- .../kgRegDetail/kgRegDetail.component.ts | 50 +++- .../kgRegDetail/kgRegDetail.template.html | 50 ++++ .../kgRegList/kgRegList.template.html | 6 +- .../kgRegList/kgReglist.directive.ts | 13 +- .../bsFeatures/kgRegionalFeature/module.ts | 2 + .../bsFeatures/kgRegionalFeature/type.ts | 5 + .../userAnnotations/tools/service.ts | 1 + src/ui/ui.module.ts | 2 - src/viewerModule/constants.ts | 6 - src/viewerModule/module.ts | 4 - .../threeSurferGlue/threeSurfer.component.ts | 10 + .../viewerCmp/viewerCmp.component.ts | 43 +--- .../viewerCmp/viewerCmp.template.html | 166 ++---------- webpack/webpack.staticassets.js | 2 +- 20 files changed, 356 insertions(+), 307 deletions(-) rename src/{viewerModule/util => atlasComponents/parcellationRegion}/regionAccordionTooltipText.pipe.ts (88%) diff --git a/Dockerfile b/Dockerfile index 80d15098c..56768d28e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ ARG DATASET_PREVIEW_URL ENV DATASET_PREVIEW_URL=${DATASET_PREVIEW_URL:-https://hbp-kg-dataset-previewer.apps.hbp.eu/v2} ARG BS_REST_URL -ENV BS_REST_URL=${BS_REST_URL:-https://siibra-api-tmpfullvolmetadata.apps-dev.hbp.eu/v1_0} +ENV BS_REST_URL=${BS_REST_URL:-https://siibra-api-tmpxgcustom.apps-dev.hbp.eu/v1_0} ARG STRICT_LOCAL ENV STRICT_LOCAL=${STRICT_LOCAL:-false} diff --git a/deploy/datasets/index.js b/deploy/datasets/index.js index 4f63e80b8..7f55dff26 100644 --- a/deploy/datasets/index.js +++ b/deploy/datasets/index.js @@ -192,6 +192,7 @@ datasetsRouter.get('/hasPreview', cacheMaxAge24Hr, async (req, res) => { }) datasetsRouter.get('/kgInfo', checkKgQuery, cacheMaxAge24Hr, async (req, res) => { + return res.status(400).send('Deprecated') const { kgId } = req.query const { kgSchema } = req.query const { user } = req diff --git a/src/atlasComponents/parcellationRegion/module.ts b/src/atlasComponents/parcellationRegion/module.ts index 1e400b5e2..ea4826233 100644 --- a/src/atlasComponents/parcellationRegion/module.ts +++ b/src/atlasComponents/parcellationRegion/module.ts @@ -9,6 +9,9 @@ import { RegionDirective } from "./region.directive"; import { RegionListSimpleViewComponent } from "./regionListSimpleView/regionListSimpleView.component"; import { RegionMenuComponent } from "./regionMenu/regionMenu.component"; import { SimpleRegionComponent } from "./regionSimple/regionSimple.component"; +import { BSFeatureModule } from "../regionalFeatures/bsFeatures"; +import { RegionAccordionTooltipTextPipe } from "./regionAccordionTooltipText.pipe"; +import { AtlasCmptConnModule } from "../connectivity"; @NgModule({ imports: [ @@ -17,6 +20,8 @@ import { SimpleRegionComponent } from "./regionSimple/regionSimple.component"; DatabrowserModule, AngularMaterialModule, ComponentsModule, + BSFeatureModule, + AtlasCmptConnModule, ], declarations: [ RegionMenuComponent, @@ -25,6 +30,7 @@ import { SimpleRegionComponent } from "./regionSimple/regionSimple.component"; RegionDirective, RenderViewOriginDatasetLabelPipe, + RegionAccordionTooltipTextPipe, ], exports: [ RegionMenuComponent, diff --git a/src/viewerModule/util/regionAccordionTooltipText.pipe.ts b/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts similarity index 88% rename from src/viewerModule/util/regionAccordionTooltipText.pipe.ts rename to src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts index 65c5f2199..c2a92b83d 100644 --- a/src/viewerModule/util/regionAccordionTooltipText.pipe.ts +++ b/src/atlasComponents/parcellationRegion/regionAccordionTooltipText.pipe.ts @@ -1,9 +1,5 @@ import { Pipe, PipeTransform } from "@angular/core" -/** - * TODO find this pipe a home - * not too sure where this should stay - */ @Pipe({ name: 'regionAccordionTooltipTextPipe', pure: true diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts index 8d9f08545..8747535e5 100644 --- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts +++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.component.ts @@ -1,30 +1,72 @@ -import { Component, OnDestroy, Input } from "@angular/core"; +import { Component, OnDestroy } from "@angular/core"; import { Store } from "@ngrx/store"; -import { Subscription } from "rxjs"; +import { Observable, Subscription } from "rxjs"; import { RegionBase } from '../region.base' -import { ARIA_LABELS } from 'common/constants' +import { CONST } from 'common/constants' +import { ComponentStore } from "src/viewerModule/componentStore"; @Component({ selector: 'region-menu', templateUrl: './regionMenu.template.html', styleUrls: ['./regionMenu.style.css'], + providers: [ ComponentStore ] }) export class RegionMenuComponent extends RegionBase implements OnDestroy { + public CONST = CONST private subscriptions: Subscription[] = [] + public activePanelTitles$: Observable<string[]> + private activePanelTitles: string[] = [] constructor( store$: Store<any>, + private viewerCmpLocalUiStore: ComponentStore<{ activePanelsTitle: string[] }>, ) { super(store$) + this.viewerCmpLocalUiStore.setState({ + activePanelsTitle: [] + }) + + this.activePanelTitles$ = this.viewerCmpLocalUiStore.select( + state => state.activePanelsTitle + ) as Observable<string[]> + + this.subscriptions.push( + this.activePanelTitles$.subscribe( + (activePanelTitles: string[]) => this.activePanelTitles = activePanelTitles + ) + ) } ngOnDestroy(): void { this.subscriptions.forEach(s => s.unsubscribe()) } - @Input() - showRegionInOtherTmpl: boolean = true + handleExpansionPanelClosedEv(title: string){ + this.viewerCmpLocalUiStore.setState({ + activePanelsTitle: this.activePanelTitles.filter(n => n !== title) + }) + } + handleExpansionPanelAfterExpandEv(title: string){ + if (this.activePanelTitles.includes(title)) return + this.viewerCmpLocalUiStore.setState({ + activePanelsTitle: [ + ...this.activePanelTitles, + title + ] + }) + } - SHOW_IN_OTHER_REF_SPACE = ARIA_LABELS.SHOW_IN_OTHER_REF_SPACE + public busyFlag = false + private busyMap = new Map<string, boolean>() + handleBusySignal(namespace: string, flag: boolean) { + this.busyMap.set(namespace, flag) + for (const [_key, val] of this.busyMap.entries()) { + if (val) { + this.busyFlag = true + return + } + } + this.busyFlag = false + } } diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html index 8f7b7adcb..d972b2651 100644 --- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html @@ -1,4 +1,4 @@ -<mat-card> +<mat-card class="mat-elevation-z4"> <!-- rgbDarkmode must be checked for strict equality to true/false as if rgb is undefined, rgbDarkmode will be null/undefined which is falsy --> @@ -32,55 +32,6 @@ {{ regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }} </span> </ng-template> - - <span - aria-hidden="true" - [kgSchema]="originDataset.kgSchema" - [kgId]="originDataset.kgId" - single-dataset-directive - #sdDirective="singleDatasetDirective"> - </span> - - <ng-template [ngIf]="sdDirective.fetchFlag" [ngIfElse]="contentTmpl"> - <spinner-cmp></spinner-cmp> - </ng-template> - - <ng-template #contentTmpl> - - <!-- fall back if no kg ref is available --> - <a *ngIf="sdDirective.kgReference.length === 0" - [href]="sdDirective.directLinkToKg" - target="_blank"> - <button mat-icon-button - color="primary"> - <i class="fas fa-external-link-alt"></i> - </button> - </a> - - <!-- kg ref, normally doi --> - <a *ngFor="let kgRef of sdDirective.kgReference" - [href]="kgRef | doiParserPipe" - target="_blank"> - <button mat-icon-button - color="primary"> - <i class="fas fa-external-link-alt"></i> - </button> - </a> - - <!-- pin/unpin --> - <ng-container *ngTemplateOutlet="pinTmpl; context: { $implicit: sdDirective.isFav$ | async }"> - </ng-container> - - <ng-template #pinTmpl let-isFav> - - <button mat-icon-button - (click)="isFav ? sdDirective.undoableRemoveFav() : sdDirective.undoableAddFav()" - [color]="isFav ? 'primary' : 'default'"> - <i class="fas fa-thumbtack"></i> - </button> - </ng-template> - - </ng-template> </div> <mat-divider vertical="true" class="ml-2 h-2rem"></mat-divider> @@ -93,49 +44,156 @@ </mat-icon> </button> - <!-- region in other templates --> - <button mat-icon-button - *ngIf="showRegionInOtherTmpl" - [attr.data-available-in-tmpl-count]="(regionInOtherTemplates$ | async).length" - [attr.aria-label]="AVAILABILITY_IN_OTHER_REF_SPACE" - [matMenuTriggerFor]="regionInOtherTemplatesMenu" - [matMenuTriggerData]="{ regionInOtherTemplates: regionInOtherTemplates$ | async }"> - <i class="fas fa-globe"></i> - </button> - </mat-card-subtitle> </div> </mat-card> -<!-- template for switching template --> -<mat-menu #regionInOtherTemplatesMenu="matMenu" - [aria-label]="SHOW_IN_OTHER_REF_SPACE"> - <ng-template matMenuContent let-regionInOtherTemplates="regionInOtherTemplates"> - - <mat-list-item *ngFor="let sameRegion of regionInOtherTemplates; let i = index" - [attr.aria-label]="SHOW_IN_OTHER_REF_SPACE + ': ' + sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '') " - (click)="changeView(sameRegion)" - mat-ripple - [attr.role]="'button'"> - <mat-icon fontSet="fas" fontIcon="fa-none" mat-list-icon></mat-icon> - <div mat-line> - <ng-container *ngTemplateOutlet="regionInOtherTemplate; context: sameRegion"> - </ng-container> - </div> - </mat-list-item> +<mat-accordion class="d-block mt-2"> + + <!-- receptor --> + <div bs-features-receptor-directive + [region]="region" + #bsFeatureReceptorDirective="bsFeatureReceptorDirective"> + </div> + <ng-template #regionalReceptorTmpl> + <bs-features-receptor-entry [region]="region"> + </bs-features-receptor-entry> + </ng-template> + <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { + title: 'ReceptorDistribution', + iconClass: 'fas fa-info', + iavNgIf: bsFeatureReceptorDirective.hasReceptor$ | async, + content: regionalReceptorTmpl + }"> + </ng-container> + + + <!-- Explore in other template --> + <ng-container *ngIf="regionInOtherTemplates$ | async as regionInOtherTemplates"> + + <ng-template #exploreInOtherTmpl> + <mat-card *ngFor="let sameRegion of regionInOtherTemplates" + class="p-0 border-0 box-shadow-none mt-1 tb-1 cursor-pointer" + (click)="changeView(sameRegion)" + [matTooltip]="sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '')" + mat-ripple> + <small> + {{ sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '') }} + </small> + </mat-card> + </ng-template> + + <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { + title: 'Explore in other templates', + desc: regionInOtherTemplates.length, + iconClass: 'fas fa-brain', + iconTooltip: regionInOtherTemplates.length | regionAccordionTooltipTextPipe : 'regionInOtherTmpl', + iavNgIf: regionInOtherTemplates.length, + content: exploreInOtherTmpl + }"> + + + </ng-container> + </ng-container> + + <!-- kg regional features list --> + <ng-template #kgRegionalFeatureList> + <kg-regional-features-list [region]="region"> + </kg-regional-features-list> + </ng-template> + + <div kg-regional-features-list-directive + [region]="region" + (kg-regional-features-list-directive-busy)="handleBusySignal('regionFeatureList', $event)" + #kgRegFeatlist="kgRegionalFeaturesListDirective"> + </div> + + <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { + title: CONST.REGIONAL_FEATURES, + iconClass: 'fas fa-database', + content: kgRegionalFeatureList, + desc: kgRegFeatlist.kgRegionalFeatures.length, + iconTooltip: kgRegFeatlist.kgRegionalFeatures.length | regionAccordionTooltipTextPipe : 'regionalFeatures', + iavNgIf: (kgRegFeatlist.kgRegionalFeatures$ | async ).length + }"> + </ng-container> + + <!-- Connectivity --> + + <ng-template #connectivityContentTmpl let-expansionPanel="expansionPanel"> + <mat-card-content class="flex-grow-1 flex-shrink-1 w-100"> + <connectivity-browser class="pe-all flex-shrink-1" + [region]="region" + (setOpenState)="expansionPanel.expanded = $event" + [accordionExpanded]="expansionPanel.expanded" + (connectivityNumberReceived)="hasConnectivityDirective.connectivityNumber = $event"> + </connectivity-browser> + </mat-card-content> </ng-template> -</mat-menu> - -<!-- template for rendering template name and template hemisphere --> -<ng-template #regionInOtherTemplate let-template="template" let-hemisphere="hemisphere"> - <span class="overflow-x-hidden text-truncate" - [matTooltip]="template.name + (hemisphere ? (' ' + hemisphere) : '')"> - <span> - {{ template.name }} - </span> - <span *ngIf="hemisphere" class="text-muted"> - ({{ hemisphere }}) - </span> - </span> + + <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { + title: 'Connectivity', + desc: hasConnectivityDirective.connectivityNumber, + iconClass: 'fas fa-braille', + iconTooltip: hasConnectivityDirective.connectivityNumber | regionAccordionTooltipTextPipe : 'connectivity', + iavNgIf: hasConnectivityDirective.hasConnectivity, + content: connectivityContentTmpl + }"> + </ng-container> + + <div has-connectivity + [region]="[region]" + #hasConnectivityDirective="hasConnectivityDirective"> + </div> +</mat-accordion> + +<div *ngIf="busyFlag" class="mt-2 d-flex justify-content-center"> + <spinner-cmp></spinner-cmp> +</div> + +<!-- expansion tmpl --> +<ng-template #ngMatAccordionTmpl + let-title="title" + let-desc="desc" + let-iconClass="iconClass" + let-iconTooltip="iconTooltip" + let-iavNgIf="iavNgIf" + let-content="content"> + <mat-expansion-panel + [expanded]="activePanelTitles$ | async | arrayContains : title" + [attr.data-opened]="expansionPanel.expanded" + [attr.data-mat-expansion-title]="title" + (closed)="handleExpansionPanelClosedEv(title)" + (afterExpand)="handleExpansionPanelAfterExpandEv(title)" + hideToggle + *ngIf="iavNgIf" + #expansionPanel="matExpansionPanel"> + + <mat-expansion-panel-header> + + <!-- title --> + <mat-panel-title> + {{ title }} + </mat-panel-title> + + <!-- desc + icon --> + <mat-panel-description class="d-flex align-items-center justify-content-end" + [matTooltip]="iconTooltip"> + <span class="mr-3">{{ desc }}</span> + <span class="accordion-icon d-inline-flex justify-content-center"> + <i [class]="iconClass"></i> + </span> + </mat-panel-description> + + </mat-expansion-panel-header> + + <!-- content --> + <ng-template matExpansionPanelContent> + <ng-container *ngTemplateOutlet="content; context: { + expansionPanel: expansionPanel + }"> + </ng-container> + </ng-template> + </mat-expansion-panel> </ng-template> diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.component.ts index 54fcf016d..4a7d79ddd 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.component.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.component.ts @@ -1,6 +1,13 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input, OnChanges } from "@angular/core"; import { BsRegionInputBase } from "../../bsRegionInputBase"; -import { TBSDetail } from "../type"; +import { KG_REGIONAL_FEATURE_KEY, TBSDetail, UNDER_REVIEW } from "../type"; +import { ARIA_LABELS, CONST } from 'common/constants' +import { TBSSummary } from "../../kgDataset"; +import { BsFeatureService } from "../../service"; + +/** + * this component is specifically used to render side panel ebrains dataset view + */ @Component({ selector: 'kg-regional-feature-detail', @@ -10,8 +17,45 @@ import { TBSDetail } from "../type"; ] }) -export class KgRegDetailCmp extends BsRegionInputBase { +export class KgRegDetailCmp extends BsRegionInputBase implements OnChanges { + + public ARIA_LABELS = ARIA_LABELS + public CONST = CONST + + @Input() + public summary: TBSSummary @Input() public detail: TBSDetail + + public loadingFlag = false + public error = null + + public nameFallback = `[This dataset cannot be fetched right now]` + public isGdprProtected = false + + public descriptionFallback = `[This dataset cannot be fetched right now]` + + constructor(svc: BsFeatureService){ + super(svc) + } + + ngOnChanges(){ + if (!this.region) return + if (!this.summary) return + if (!!this.detail) return + this.loadingFlag = true + this.getFeatureInstance(KG_REGIONAL_FEATURE_KEY, this.summary['@id']).subscribe( + detail => { + this.detail = detail + this.isGdprProtected = detail.__detail.embargoStatus && detail.__detail.embargoStatus.some(status => status["@id"] === UNDER_REVIEW["@id"]) + }, + err => { + this.error = err.toString() + }, + () => { + this.loadingFlag = false + } + ) + } } diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.template.html index e69de29bb..7df120ded 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.template.html +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegDetail/kgRegDetail.template.html @@ -0,0 +1,50 @@ +<!-- header --> + +<mat-card class="mat-elevation-z4"> + + <div class="sidenav-cover-header-container bg-50-grey-20"> + <mat-card-title> + <ng-content select="[region-of-interest]"></ng-content> + <div *ngIf="!loadingFlag; else isLoadingTmpl"> + {{ (detail && detail.src_name) || nameFallback }} + </div> + </mat-card-title> + + <mat-card-subtitle class="d-inline-flex align-items-center"> + <mat-icon fontSet="fas" fontIcon="fa-database"></mat-icon> + <span> + ebrains regional dataset + </span> + + <button *ngIf="isGdprProtected" + [matTooltip]="CONST.GDPR_TEXT" + mat-icon-button color="warn"> + <i class="fas fa-exclamation-triangle"></i> + </button> + + <mat-divider [vertical]="true" class="ml-2 h-2rem"></mat-divider> + + <!-- explore btn --> + <a *ngFor="let kgRef of (detail?.__detail?.kgReference || [])" + [href]="kgRef | doiParserPipe" + class="color-inherit" + mat-icon-button + [matTooltip]="ARIA_LABELS.EXPLORE_DATASET_IN_KG" + target="_blank"> + <i class="fas fa-external-link-alt"></i> + </a> + + </mat-card-subtitle> + </div> + +</mat-card> + +<!-- description --> + +<markdown-dom class="text-muted d-block mat-body m-4" *ngIf="!loadingFlag" + [markdown]="detail?.__detail?.description || descriptionFallback"> +</markdown-dom> + +<ng-template #isLoadingTmpl> + <spinner-cmp></spinner-cmp> +</ng-template> diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html index 1cc27fde7..d8d99fe6e 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html @@ -59,15 +59,15 @@ class="virtual-scroll-element overflow-hidden"> <!-- divider, show if not first --> - <mat-divider *ngIf="index !== 0"></mat-divider> + <mat-divider class="mt-1" *ngIf="index !== 0"></mat-divider> <kg-regional-feature-summary mat-ripple iav-dataset-show-dataset-dialog - [iav-dataset-show-dataset-dialog-kgid]="dataset['@id'] | getTrailingHex" + [iav-dataset-show-dataset-dialog-fullid]="dataset['@id']" class="d-block pb-1 pt-1" [region]="region" - [loadFull]="true" + [loadFull]="false" [summary]="dataset" (loadedDetail)="handlePopulatedDetailEv($event)"> </kg-regional-feature-summary> diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts index 78e73c9e3..53d4ce42a 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts @@ -1,4 +1,4 @@ -import { Directive, OnDestroy } from "@angular/core"; +import { Directive, EventEmitter, OnDestroy, Output } from "@angular/core"; import { KG_REGIONAL_FEATURE_KEY, TBSSummary } from "../type"; import { BsFeatureService } from "../../service"; import { BsRegionInputBase } from "../../bsRegionInputBase"; @@ -15,9 +15,15 @@ export class KgRegionalFeaturesListDirective extends BsRegionInputBase implement public kgRegionalFeatures$ = this.region$.pipe( filter(v => !!v), // must not use switchmapto here - switchMap(() => this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY)), + switchMap(() => { + this.busyEmitter.emit(true) + return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe( + tap(() => this.busyEmitter.emit(false)) + ) + }), startWith([]) ) + constructor(svc: BsFeatureService){ super(svc) this.sub.push( @@ -30,4 +36,7 @@ export class KgRegionalFeaturesListDirective extends BsRegionInputBase implement ngOnDestroy(){ while (this.sub.length) this.sub.pop().unsubscribe() } + + @Output('kg-regional-features-list-directive-busy') + busyEmitter = new EventEmitter<boolean>() } \ No newline at end of file diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts index 79ecb08fd..73bbbf881 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts @@ -8,6 +8,7 @@ import { KgRegDetailCmp } from "./kgRegDetail/kgRegDetail.component"; import { KgDatasetModule } from "../kgDataset"; import { IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "../kgDataset/showDataset/showDataset.directive"; import { UtilModule } from "src/util"; +import { ComponentsModule } from "src/components"; @NgModule({ imports: [ @@ -15,6 +16,7 @@ import { UtilModule } from "src/util"; AngularMaterialModule, KgDatasetModule, UtilModule, + ComponentsModule, ], declarations:[ KgRegSummaryCmp, diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts index 371a926c2..beb275754 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/type.ts @@ -3,3 +3,8 @@ export { } from '../kgDataset' export const KG_REGIONAL_FEATURE_KEY = 'EbrainsRegionalDataset' + +export const UNDER_REVIEW = { + ['@id']: "https://nexus.humanbrainproject.org/v0/data/minds/core/embargostatus/v1.0.0/1d726b76-b176-47ed-96f0-b4f2e17d5f19" +} + diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts index ab789a0d3..6652adb9c 100644 --- a/src/atlasComponents/userAnnotations/tools/service.ts +++ b/src/atlasComponents/userAnnotations/tools/service.ts @@ -548,6 +548,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{ arr.push(json) } const stringifiedJSON = JSON.stringify(arr) + if (!(window as any).export_nehuba) return const { pako } = (window as any).export_nehuba const compressed = pako.deflate(stringifiedJSON) let out = '' diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 7145621bc..ff474d2af 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -48,7 +48,6 @@ import { Landmark2DModule } from "./nehubaContainer/2dLandmarks/module"; import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "./screenshot"; import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion"; import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation"; -import { AtlasCmptConnModule } from "src/atlasComponents/connectivity"; @NgModule({ imports : [ @@ -68,7 +67,6 @@ import { AtlasCmptConnModule } from "src/atlasComponents/connectivity"; Landmark2DModule, ParcellationRegionModule, AtlasCmpParcellationModule, - AtlasCmptConnModule, ], declarations : [ diff --git a/src/viewerModule/constants.ts b/src/viewerModule/constants.ts index aa974a56e..8fa2d2523 100644 --- a/src/viewerModule/constants.ts +++ b/src/viewerModule/constants.ts @@ -2,9 +2,3 @@ import { InjectionToken } from "@angular/core"; import { Observable } from "rxjs"; export const VIEWERMODULE_DARKTHEME = new InjectionToken<Observable<boolean>>('VIEWERMODULE_DARKTHEME') - -export interface IViewerCmpUiState { - sideNav: { - activePanelsTitle: string[] - } -} diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts index 3c74f7b74..91620bb87 100644 --- a/src/viewerModule/module.ts +++ b/src/viewerModule/module.ts @@ -1,7 +1,6 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { Observable } from "rxjs"; -import { AtlasCmptConnModule } from "src/atlasComponents/connectivity"; import { DatabrowserModule } from "src/atlasComponents/databrowserModule"; import { AtlasCmpParcellationModule } from "src/atlasComponents/parcellation"; import { ParcellationRegionModule } from "src/atlasComponents/parcellationRegion"; @@ -17,7 +16,6 @@ import { CONTEXT_MENU_ITEM_INJECTOR, TContextMenu, UtilModule } from "src/util"; import { VIEWERMODULE_DARKTHEME } from "./constants"; import { NehubaModule, NehubaViewerUnit } from "./nehuba"; import { ThreeSurferModule } from "./threeSurfer"; -import { RegionAccordionTooltipTextPipe } from "./util/regionAccordionTooltipText.pipe"; import { ViewerCmp } from "./viewerCmp/viewerCmp.component"; import {UserAnnotationsModule} from "src/atlasComponents/userAnnotations"; import {QuickTourModule} from "src/ui/quickTour/module"; @@ -40,7 +38,6 @@ import { TContextArg } from "./viewer.interface"; ParcellationRegionModule, UtilModule, AtlasCmpParcellationModule, - AtlasCmptConnModule, ComponentsModule, BSFeatureModule, UserAnnotationsModule, @@ -49,7 +46,6 @@ import { TContextArg } from "./viewer.interface"; ], declarations: [ ViewerCmp, - RegionAccordionTooltipTextPipe, ], providers: [ { diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts index db5e39b07..e4cc4fa88 100644 --- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts +++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts @@ -69,6 +69,15 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af this.selectedMode = mode.name const { meshes } = mode + await retry(async () => { + for (const singleMesh of meshes) { + const { hemisphere } = singleMesh + if (!this.regionMap.has(hemisphere)) throw new Error(`regionmap does not have hemisphere defined!`) + } + }, { + timeout: 32, + retries: 10 + }) for (const singleMesh of meshes) { const { mesh, colormap, hemisphere } = singleMesh this.allKeys.push({name: hemisphere, checked: true}) @@ -77,6 +86,7 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, OnChanges, Af parseContext(mesh, [this.config['@context']]) ) + if (!this.regionMap.has(hemisphere)) continue const rMap = this.regionMap.get(hemisphere) const applyCM = new Map() for (const [ lblIdx, region ] of rMap.entries()) { diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts index c5b886f93..d7109e888 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.component.ts +++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts @@ -19,12 +19,8 @@ import { uiActionHideAllDatasets, uiActionHideDatasetWithId, uiActionShowDataset import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, REGION_OF_INTEREST } from "src/util/interfaces"; import { animate, state, style, transition, trigger } from "@angular/animations"; import { SwitchDirective } from "src/util/directives/switch.directive"; -import { IViewerCmpUiState } from "../constants"; import { QuickTourThis, IQuickTourData } from "src/ui/quickTour"; import { MatDrawer } from "@angular/material/sidenav"; -import { ComponentStore } from "../componentStore"; -import {BS_ENDPOINT} from "src/util/constants"; -import {HttpClient} from "@angular/common/http"; import { PureContantService } from "src/util"; import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../viewer.interface"; import { getGetRegionFromLabelIndexId } from "src/util/fn"; @@ -98,7 +94,6 @@ import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule"; }, deps: [ Store ] }, - ComponentStore ] }) @@ -224,24 +219,9 @@ export class ViewerCmp implements OnDestroy { constructor( private store$: Store<any>, - private viewerCmpLocalUiStore: ComponentStore<IViewerCmpUiState>, private viewerModuleSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>, @Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any> ){ - this.viewerCmpLocalUiStore.setState({ - sideNav: { - activePanelsTitle: [] - } - }) - - this.activePanelTitles$ = this.viewerCmpLocalUiStore.select( - state => state.sideNav.activePanelsTitle - ) as Observable<string[]> - this.subscriptions.push( - this.activePanelTitles$.subscribe( - (activePanelTitles: string[]) => this.activePanelTitles = activePanelTitles - ) - ) this.subscriptions.push( this.alwaysHideMinorPanel$.pipe( @@ -334,27 +314,6 @@ export class ViewerCmp implements OnDestroy { while (this.onDestroyCb.length > 0) this.onDestroyCb.pop()() } - public activePanelTitles$: Observable<string[]> - private activePanelTitles: string[] = [] - handleExpansionPanelClosedEv(title: string){ - this.viewerCmpLocalUiStore.setState({ - sideNav: { - activePanelsTitle: this.activePanelTitles.filter(n => n !== title) - } - }) - } - handleExpansionPanelAfterExpandEv(title: string){ - if (this.activePanelTitles.includes(title)) return - this.viewerCmpLocalUiStore.setState({ - sideNav: { - activePanelsTitle: [ - ...this.activePanelTitles, - title - ] - } - }) - } - public bindFns(fns){ return () => { for (const [ fn, ...arg] of fns) { @@ -411,7 +370,7 @@ export class ViewerCmp implements OnDestroy { }}) ) } - public clearPreviewingDataset(id: string){ + public clearPreviewingDataset(id?: string){ /** * clear all preview */ diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index 63a2320d7..4bcccb6e1 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -113,7 +113,7 @@ <iav-layout-fourcorners [iav-layout-fourcorners-cnr-cntr-ngclass]="{'w-100': true}"> - <!-- pullable tab top right corner --> + <!-- pullable tab top left corner --> <div iavLayoutFourCornersTopLeft class="d-flex flex-nowrap w-100"> <!-- top left --> @@ -205,26 +205,26 @@ <ng-container *ngIf="iavShownDataset.shownDatasetId$ | async as shownDatasetId"> <ng-template [ngIf]="shownDatasetId.length > 0" [ngIfElse]="sideNavVolumePreview"> - <!-- single dataset side nav panel --> - <single-dataset-sidenav-view *ngFor="let id of shownDatasetId" - (clear)="clearPreviewingDataset(id)" - [fullId]="id" - class="bs-border-box ml-15px-n mr-15px-n"> - <mat-chip *ngIf="regionOfInterest$ && regionOfInterest$ | async as region" - region-of-interest - iav-region - [region]="region" - [ngClass]="{ - 'darktheme':regionDirective.rgbDarkmode === true, - 'lighttheme': regionDirective.rgbDarkmode === false - }" - [style.backgroundColor]="regionDirective.rgbString" - #regionDirective="iavRegion"> - <span class="iv-custom-comp text text-truncate d-inline"> - {{ region.name }} + <div class="position-relative ml-15px-n mr-15px-n"> + + <!-- back btn --> + <button mat-button + (click)="clearPreviewingDataset()" + [attr.aria-label]="ARIA_LABELS.CLOSE" + class="position-absolute z-index-10 m-2"> + <i class="fas fa-chevron-left"></i> + <span class="ml-1"> + Back </span> - </mat-chip> - </single-dataset-sidenav-view> + </button> + + <!-- ebrains region --> + <kg-regional-feature-detail + *ngFor="let datasetId of shownDatasetId" + [summary]="{'@id': datasetId}" + [region]="selectedRegions$ | async | getNthElement : 0"> + </kg-regional-feature-detail> + </div> </ng-template> </ng-container> @@ -695,126 +695,10 @@ <!-- region detail --> <ng-container *ngIf="region; else regionPlaceholderTmpl"> <region-menu - [showRegionInOtherTmpl]="false" [region]="region" - class="bs-border-box ml-15px-n mr-15px-n mat-elevation-z4"> + class="flex-grow-1 bs-border-box ml-15px-n mr-15px-n mat-elevation-z4"> </region-menu> </ng-container> - - <!-- other region detail accordion --> - <mat-accordion *ngIf="region" - class="bs-border-box ml-15px-n mr-15px-n mt-2" - iav-region - [region]="region" - #iavRegion="iavRegion"> - - <!-- desc --> - <!-- obsolete --> - - <!-- Explore in other template --> - <ng-container *ngIf="iavRegion.regionInOtherTemplates$ | async as regionInOtherTemplates"> - - <ng-template #exploreInOtherTmpl> - <mat-card *ngFor="let sameRegion of regionInOtherTemplates" - class="p-0 border-0 box-shadow-none mt-1 tb-1 cursor-pointer" - (click)="iavRegion.changeView(sameRegion)" - [matTooltip]="sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '')" - mat-ripple> - <small> - {{ sameRegion.template.name + (sameRegion.hemisphere ? (' - ' + sameRegion.hemisphere) : '') }} - </small> - </mat-card> - </ng-template> - - <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { - title: 'Explore in other templates', - desc: regionInOtherTemplates.length, - iconClass: 'fas fa-brain', - iconTooltip: regionInOtherTemplates.length | regionAccordionTooltipTextPipe : 'regionInOtherTmpl', - iavNgIf: regionInOtherTemplates.length, - content: exploreInOtherTmpl - }"> - - - </ng-container> - </ng-container> - - <!-- tmp experimtal --> - <ng-template #regionalReceptorTmpl> - <bs-features-receptor-entry - [region]="region"> - </bs-features-receptor-entry> - </ng-template> - - <div bs-features-receptor-directive - [region]="region" - #bsFeatureReceptorDirective="bsFeatureReceptorDirective"> - </div> - <spinner-cmp *ngIf="bsFeatureReceptorDirective.fetching$ | async"></spinner-cmp> - <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { - title: 'ReceptorDistribution', - iconClass: 'fas fa-info', - iavNgIf: bsFeatureReceptorDirective.hasReceptor$ | async, - content: regionalReceptorTmpl - }"> - - </ng-container> - - <!-- kg regional features list --> - <ng-template #kgRegionalFeatureList> - <kg-regional-features-list [region]="region"> - </kg-regional-features-list> - </ng-template> - - <div kg-regional-features-list-directive - [region]="region" - #kgRegFeatlist="kgRegionalFeaturesListDirective"> - </div> - - <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { - title: CONST.REGIONAL_FEATURES, - iconClass: 'fas fa-database', - content: kgRegionalFeatureList, - desc: kgRegFeatlist.kgRegionalFeatures.length, - iconTooltip: kgRegFeatlist.kgRegionalFeatures.length | regionAccordionTooltipTextPipe : 'regionalFeatures', - iavNgIf: (kgRegFeatlist.kgRegionalFeatures$ | async ).length - }"> - </ng-container> - - <!-- Connectivity --> - <ng-container *ngIf="parcellationSelected$ | async as selectedParcellation"> - - <ng-template #connectivityContentTmpl let-expansionPanel="expansionPanel"> - <mat-card-content class="flex-grow-1 flex-shrink-1 w-100"> - <ng-container *ngFor="let region of selectedRegions$ | async"> - <connectivity-browser class="pe-all flex-shrink-1" - [region]="region" - (setOpenState)="expansionPanel.expanded = $event" - [accordionExpanded]="expansionPanel.expanded" - (connectivityNumberReceived)="hasConnectivityDirective.connectivityNumber = $event"> - </connectivity-browser> - </ng-container> - </mat-card-content> - </ng-template> - - <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { - title: 'Connectivity', - desc: hasConnectivityDirective.connectivityNumber, - iconClass: 'fas fa-braille', - iconTooltip: hasConnectivityDirective.connectivityNumber | regionAccordionTooltipTextPipe : 'connectivity', - iavNgIf: hasConnectivityDirective.hasConnectivity, - content: connectivityContentTmpl - }"> - </ng-container> - - <div has-connectivity - [region]="selectedRegions$ | async" - #hasConnectivityDirective="hasConnectivityDirective"> - </div> - - </ng-container> - - </mat-accordion> </ng-template> @@ -827,11 +711,8 @@ let-iavNgIf="iavNgIf" let-content="content"> <mat-expansion-panel - [expanded]="activePanelTitles$ | async | arrayContains : title" [attr.data-opened]="expansionPanel.expanded" [attr.data-mat-expansion-title]="title" - (closed)="handleExpansionPanelClosedEv(title)" - (afterExpand)="handleExpansionPanelAfterExpandEv(title)" hideToggle *ngIf="iavNgIf" #expansionPanel="matExpansionPanel"> @@ -883,10 +764,7 @@ <ng-template #multiRegionTmpl let-regions="regions"> <ng-template [ngIf]="regions.length > 0" [ngIfElse]="regionPlaceholderTmpl"> <region-menu - [showRegionInOtherTmpl]="false" - [region]="{ - name: CONST.MULTI_REGION_SELECTION - }" + [region]="{ name: CONST.MULTI_REGION_SELECTION }" class="bs-border-box ml-15px-n mr-15px-n mat-elevation-z4"> </region-menu> diff --git a/webpack/webpack.staticassets.js b/webpack/webpack.staticassets.js index d187f6b5a..ac4863ef5 100644 --- a/webpack/webpack.staticassets.js +++ b/webpack/webpack.staticassets.js @@ -67,7 +67,7 @@ module.exports = { PRODUCTION: !!process.env.PRODUCTION, BACKEND_URL: (process.env.BACKEND_URL && JSON.stringify(process.env.BACKEND_URL)) || 'null', DATASET_PREVIEW_URL: JSON.stringify(process.env.DATASET_PREVIEW_URL || 'https://hbp-kg-dataset-previewer.apps.hbp.eu/v2'), - BS_REST_URL: JSON.stringify(process.env.BS_REST_URL || 'https://siibra-api-tmpfullvolmetadata.apps-dev.hbp.eu/v1_0'), + BS_REST_URL: JSON.stringify(process.env.BS_REST_URL || 'https://siibra-api-tmpxgcustom.apps-dev.hbp.eu/v1_0'), SPATIAL_TRANSFORM_BACKEND: JSON.stringify(process.env.SPATIAL_TRANSFORM_BACKEND || 'https://hbp-spatial-backend.apps.hbp.eu'), MATOMO_URL: JSON.stringify(process.env.MATOMO_URL || null), MATOMO_ID: JSON.stringify(process.env.MATOMO_ID || null), -- GitLab