From 340539092390a78961c114064dd49cb8fbb40566 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Mon, 4 Oct 2021 15:50:50 +0200 Subject: [PATCH] feat: added no. ann badge feat: delete all ann btn feat: loading ann feedback --- common/constants.js | 8 ++- docs/releases/v2.4.8.md | 6 +++ .../annotationList.component.ts | 49 +++++++++++++++++-- .../annotationList.template.html | 9 ++++ .../confirmDialog/confirmDialog.component.ts | 5 +- .../confirmDialog/confirmDialog.template.html | 2 +- src/services/dialogService.service.ts | 19 ++++++- .../viewerCmp/viewerCmp.template.html | 12 +++-- 8 files changed, 98 insertions(+), 12 deletions(-) diff --git a/common/constants.js b/common/constants.js index 64e7626ba..c882aaec1 100644 --- a/common/constants.js +++ b/common/constants.js @@ -70,7 +70,8 @@ USER_ANNOTATION_HIDE: 'user annotations hide', USER_ANNOTATION_DELETE: 'Delete annotation', GOTO_ANNOTATION_ROI: 'Navigate to annotation location of interest', - EXIT_ANNOTATION_MODE: 'Exit annotation mode' + EXIT_ANNOTATION_MODE: 'Exit annotation mode', + BULK_DELETE_ANNOTATIONS: 'Delete all user annotations' } exports.IDS = { @@ -79,6 +80,8 @@ } exports.CONST = { + LOADING_TXT: `Loading ...`, + CANNOT_DECIPHER_HEMISPHERE: 'Cannot decipher region hemisphere.', DOES_NOT_SUPPORT_MULTI_REGION_SELECTION: `Please only select a single region.`, MULTI_REGION_SELECTION: `Multi region selection`, @@ -103,6 +106,9 @@ QUICKTOUR_OK: `Start`, QUICKTOUR_NEXTTIME: `Not now`, QUICKTOUR_CANCEL: `Dismiss`, + + DELETE_ALL_ANNOTATION_CONFIRMATION_MSG: `Are you sure you want to delete all annotations?`, + LOADING_ANNOTATION_MSG: `Loading annotations... Please wait...` } exports.QUICKTOUR_DESC ={ diff --git a/docs/releases/v2.4.8.md b/docs/releases/v2.4.8.md index a448d53a7..b55bca636 100644 --- a/docs/releases/v2.4.8.md +++ b/docs/releases/v2.4.8.md @@ -1,5 +1,11 @@ # v2.4.8 +## Enhancements + +- Added badges to annotation tab button to show number of annotations added (#1007) +- Added some feedbacks when annotations are being loaded +- Added delete all annotation button + ## Bugfixes ## Under the hood stuff diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts b/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts index f3b0e82f7..91f2fd090 100644 --- a/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts +++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.component.ts @@ -1,15 +1,16 @@ -import {Component, ViewChild} from "@angular/core"; -import {ARIA_LABELS} from "common/constants"; +import { Component, Optional, ViewChild } from "@angular/core"; +import { ARIA_LABELS, CONST } from "common/constants"; import { ModularUserAnnotationToolService } from "../tools/service"; import { IAnnotationGeometry, TExportFormats } from "../tools/type"; import { ComponentStore } from "src/viewerModule/componentStore"; import { map, shareReplay, startWith } from "rxjs/operators"; -import { Observable } from "rxjs"; +import { Observable, Subscription } from "rxjs"; import { TZipFileConfig } from "src/zipFilesOutput/type"; import { TFileInputEvent } from "src/getFileInput/type"; import { FileInputDirective } from "src/getFileInput/getFileInput.directive"; import { MatSnackBar } from "@angular/material/snack-bar"; import { unzip } from "src/zipFilesOutput/zipFilesOutput.directive"; +import { DialogService } from "src/services/dialogService.service"; const README = `{id}.sands.json file contains the data of annotations. {id}.desc.json contains the metadata of annotations.` @@ -26,12 +27,17 @@ export class AnnotationList { public ARIA_LABELS = ARIA_LABELS - @ViewChild(FileInputDirective) fileInput: FileInputDirective + private subs: Subscription[] = [] + private managedAnnotations: IAnnotationGeometry[] = [] public managedAnnotations$ = this.annotSvc.spaceFilteredManagedAnnotations$ + public badge$ = this.managedAnnotations$.pipe( + map(mann => mann.length > 0 ? mann.length : null) + ) + public manAnnExists$ = this.managedAnnotations$.pipe( map(arr => !!arr && arr.length > 0), startWith(false) @@ -64,10 +70,15 @@ export class AnnotationList { private annotSvc: ModularUserAnnotationToolService, private snackbar: MatSnackBar, cStore: ComponentStore<{ useFormat: TExportFormats }>, + @Optional() private dialogSvc: DialogService, ) { cStore.setState({ useFormat: 'sands' }) + + this.subs.push( + this.managedAnnotations$.subscribe(anns => this.managedAnnotations = anns) + ) } public hiddenAnnotations$ = this.annotSvc.hiddenAnnotations$ @@ -82,6 +93,11 @@ export class AnnotationList { } async handleImportEvent(ev: TFileInputEvent<'text' | 'file'>){ + + const { abort } = this.dialogSvc.blockUserInteraction({ + title: CONST.LOADING_TXT, + markdown: CONST.LOADING_ANNOTATION_MSG, + }) try { const clearFileInputAndInform = () => { if (this.fileInput) { @@ -135,6 +151,31 @@ export class AnnotationList { this.snackbar.open(`Error importing: ${e.toString()}`, 'Dismiss', { duration: 3000 }) + } finally { + abort() + } + } + + async deleteAllAnnotation(){ + if (this.dialogSvc) { + try { + await this.dialogSvc.getUserConfirm({ + markdown: CONST.DELETE_ALL_ANNOTATION_CONFIRMATION_MSG + }) + + for (const ann of this.managedAnnotations) { + ann.remove() + } + } catch (e) { + // aborted + } + } else { + if (window.confirm(CONST.DELETE_ALL_ANNOTATION_CONFIRMATION_MSG)) { + + for (const ann of this.managedAnnotations) { + ann.remove() + } + } } } } diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html index 2bfd568c0..00bdaed21 100644 --- a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html +++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html @@ -33,6 +33,15 @@ [disabled]="!(manAnnExists$ | async)"> <i class="fas fa-download"></i> </button> + + <!-- delete all annotations --> + <button mat-icon-button + color="warn" + (click)="deleteAllAnnotation()" + [matTooltip]="ARIA_LABELS.BULK_DELETE_ANNOTATIONS" + [disabled]="!(manAnnExists$ | async)"> + <i class="fas fa-trash"></i> + </button> </mat-card-subtitle> </div> diff --git a/src/components/confirmDialog/confirmDialog.component.ts b/src/components/confirmDialog/confirmDialog.component.ts index 5c3d9eb02..ec2e20c9f 100644 --- a/src/components/confirmDialog/confirmDialog.component.ts +++ b/src/components/confirmDialog/confirmDialog.component.ts @@ -25,12 +25,15 @@ export class ConfirmDialogComponent { @Input() public markdown: string + public hideActionBar = false + constructor(@Inject(MAT_DIALOG_DATA) data: any) { - const { title = null, message = null, markdown, okBtnText, cancelBtnText} = data || {} + const { title = null, message = null, markdown, okBtnText, cancelBtnText, hideActionBar} = data || {} if (title) this.title = title if (message) this.message = message if (markdown) this.markdown = markdown if (okBtnText) this.okBtnText = okBtnText if (cancelBtnText) this.cancelBtnText = cancelBtnText + if (hideActionBar) this.hideActionBar = hideActionBar } } diff --git a/src/components/confirmDialog/confirmDialog.template.html b/src/components/confirmDialog/confirmDialog.template.html index 401261f1a..f5f054946 100644 --- a/src/components/confirmDialog/confirmDialog.template.html +++ b/src/components/confirmDialog/confirmDialog.template.html @@ -17,7 +17,7 @@ <mat-divider></mat-divider> -<mat-dialog-actions class="justify-content-start flex-row-reverse"> +<mat-dialog-actions *ngIf="!hideActionBar" class="justify-content-start flex-row-reverse"> <button [mat-dialog-close]="true" mat-raised-button color="primary">{{ okBtnText }}</button> <button [mat-dialog-close]="false" mat-button>{{ cancelBtnText }}</button> </mat-dialog-actions> diff --git a/src/services/dialogService.service.ts b/src/services/dialogService.service.ts index c202eda58..4426ef8c7 100644 --- a/src/services/dialogService.service.ts +++ b/src/services/dialogService.service.ts @@ -3,6 +3,10 @@ import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDial import { DialogComponent } from "src/components/dialog/dialog.component"; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; +type TCancellable = { + abort: () => void +} + @Injectable({ providedIn: 'root', }) @@ -16,13 +20,26 @@ export class DialogService { } + public blockUserInteraction(config: Partial<DialogConfig>): TCancellable { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + data: { + ...config, + hideActionBar: true + }, + hasBackdrop: true, + disableClose: true + }) + const abort = () => dialogRef.close() + return { abort } + } + public getUserConfirm(config: Partial<DialogConfig> = {}): Promise<string> { this.confirmDialogRef = this.dialog.open(ConfirmDialogComponent, { data: config, }) return new Promise((resolve, reject) => this.confirmDialogRef.afterClosed() .subscribe(val => { - if (val) { resolve() } else { reject('User cancelled') } + if (val) { resolve('') } else { reject('User cancelled') } }, reject, () => this.confirmDialogRef = null)) diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index 20997801b..ca92a5a01 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -16,7 +16,7 @@ [autoFocus]="false" [disableClose]="true" class="p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2"> - <annotation-list></annotation-list> + <annotation-list #annListCmp="annotationListCmp"></annotation-list> </mat-drawer> <mat-drawer-content class="visible position-relative pe-none"> @@ -30,7 +30,8 @@ matColor: 'primary', fontIcon: 'fa-list', tooltip: 'Annotation list', - click: annotationDrawer.toggle.bind(annotationDrawer) + click: annotationDrawer.toggle.bind(annotationDrawer), + badge: annListCmp?.badge$ | async }"> </ng-container> </div> @@ -58,7 +59,6 @@ </mat-drawer-content> </mat-drawer-container> - <!-- <annotation-message></annotation-message> --> </div> <!-- top drawer --> @@ -390,6 +390,8 @@ let-customColor="customColor" let-customColorDarkmode="customColorDarkmode" let-tooltip="tooltip" + let-badge="badge" + let-badgeColor="badgeColor" let-click="click"> <!-- (click)="sideNavMasterSwitch.toggle()" --> <button mat-raised-button @@ -402,7 +404,9 @@ }" (click)="click && click()" [style.backgroundColor]="customColor" - [color]="(!customColor && matColor) ? matColor : null"> + [color]="(!customColor && matColor) ? matColor : null" + [matBadge]="badge" + [matBadgeColor]="badgeColor || 'warn'"> <span [ngClass]="{'iv-custom-comp text': !!customColor}"> <i class="fas" [ngClass]="fontIcon || 'fa-question'"></i> -- GitLab