Skip to content

Commit 2eccd8a

Browse files
Vijeinathmdelez
andauthored
feat(still-image): new still-image viewer (DEV-1150) (#792)
* open image in new tab * comment * move replace file function * download still image * doc * adapt test file * test(still-image): fix tests * test(still-image): use HttpClientTestingModule instead of HttpClientModule * style(still-image): round top corners of osd container Co-authored-by: mdelez <60604010+mdelez@users.noreply.github.com>
1 parent bddb52c commit 2eccd8a

5 files changed

Lines changed: 83 additions & 20 deletions

File tree

‎src/app/workspace/resource/representation/still-image/still-image.component.html‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,19 @@
3535
<mat-icon>more_vert</mat-icon>
3636
</button>
3737
<mat-menu #more="matMenu">
38-
<a mat-menu-item [href]="iiifUrl" target="_blank">
39-
Open file in new tab
40-
</a>
38+
<button mat-menu-item (click)="openImageInNewTab(iiifUrl)">
39+
Open IIIF file in new tab
40+
</button>
4141
<button mat-menu-item [cdkCopyToClipboard]="iiifUrl"
4242
(click)="openSnackBar('IIIF URL copied to clipboard!')">
4343
Copy IIIF URL to clipboard
4444
</button>
45+
<button mat-menu-item (click)="downloadStillImage(images[0].fileValue.fileUrl)">
46+
Download file
47+
</button>
48+
<button mat-menu-item (click)="openReplaceFileDialog()">
49+
Replace file
50+
</button>
4551
</mat-menu>
4652
<!-- empty placeholder to simulate two buttons; this helps to have the zoom buttons centered with fill-remaining-space setup -->
4753
<span class="empty-space"></span>
@@ -88,16 +94,12 @@
8894

8995
<span class="fill-remaining-space"></span>
9096

91-
<!-- action buttons: create annotation/region, replace file, fullscreen -->
97+
<!-- action buttons: create annotation/region, fullscreen -->
9298
<span>
9399
<button mat-icon-button id="DSP_OSD_DRAW_REGION" matTooltip="Draw Region" [disabled]="failedToLoad"
94100
(click)="drawButtonClicked()" [class.active]="regionDrawMode">
95101
<mat-icon svgIcon="draw_region_icon"></mat-icon>
96102
</button>
97-
<button mat-icon-button id="DSP_OSD_REPLACE_IMAGE" class="replace-image" matTooltip="Replace image"
98-
(click)="openReplaceFileDialog()">
99-
<mat-icon>cloud_upload</mat-icon>
100-
</button>
101103
<button mat-icon-button id="DSP_OSD_FULL_PAGE" matTooltip="Open in fullscreen" [disabled]="failedToLoad">
102104
<mat-icon>fullscreen</mat-icon>
103105
</button>

‎src/app/workspace/resource/representation/still-image/still-image.component.scss‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
color: $bright;
2222
background-color: $dark;
2323
height: calc(100% - 64px);
24+
border-radius: 8px 8px 0px 0px;
2425

2526
&.drawing {
2627
cursor: crosshair;
@@ -110,6 +111,14 @@
110111
z-index: 1000;
111112
}
112113

114+
/*
115+
Openseadragon styling
116+
*/
117+
118+
::ng-deep .openseadragon-container {
119+
border-radius: 8px 8px 0px 0px;
120+
}
121+
113122
/*
114123
Overlay styling
115124
*/

‎src/app/workspace/resource/representation/still-image/still-image.component.spec.ts‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { CdkCopyToClipboard } from '@angular/cdk/clipboard';
22
import { OverlayContainer } from '@angular/cdk/overlay';
33
import { HarnessLoader } from '@angular/cdk/testing';
44
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
5-
import { HttpClientModule } from '@angular/common/http';
5+
import { HttpClientTestingModule } from '@angular/common/http/testing';
66
import { Component, OnInit, ViewChild } from '@angular/core';
7-
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
7+
import { ComponentFixture, TestBed } from '@angular/core/testing';
88
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
99
import { MatIconModule } from '@angular/material/icon';
1010
import { MatMenuModule } from '@angular/material/menu';
@@ -124,7 +124,7 @@ describe('StillImageComponent', () => {
124124
let rootLoader: HarnessLoader;
125125
let overlayContainer: OverlayContainer;
126126

127-
beforeEach(waitForAsync(() => {
127+
beforeEach(() => {
128128

129129
const adminSpyObj = {
130130
v2: {
@@ -140,7 +140,7 @@ describe('StillImageComponent', () => {
140140
],
141141
imports: [
142142
BrowserAnimationsModule,
143-
HttpClientModule,
143+
HttpClientTestingModule,
144144
MatDialogModule,
145145
MatIconModule,
146146
MatMenuModule,
@@ -164,7 +164,7 @@ describe('StillImageComponent', () => {
164164
]
165165
})
166166
.compileComponents();
167-
}));
167+
});
168168

169169
beforeEach(() => {
170170
testHostFixture = TestBed.createComponent(TestHostComponent);
@@ -173,9 +173,6 @@ describe('StillImageComponent', () => {
173173

174174
overlayContainer = TestBed.inject(OverlayContainer);
175175
rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostFixture);
176-
});
177-
178-
it('should create', () => {
179176
expect(testHostComponent).toBeTruthy();
180177
expect(testHostComponent.osdViewerComp).toBeTruthy();
181178
});

‎src/app/workspace/resource/representation/still-image/still-image.component.ts‎

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
1515
import { MatIconRegistry } from '@angular/material/icon';
1616
import { DomSanitizer } from '@angular/platform-browser';
17+
import { HttpClient, HttpHeaders } from '@angular/common/http';
1718
import {
1819
ApiResponseError,
1920
Constants,
@@ -136,6 +137,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
136137

137138
loading = true;
138139
failedToLoad = false;
140+
originalFilename: string;
139141

140142
regionDrawMode = false; // stores whether viewer is currently drawing a region
141143
private _regionDragInfo; // stores the information of the first click for drawing a region
@@ -144,6 +146,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
144146

145147
constructor(
146148
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
149+
private readonly _http: HttpClient,
147150
private _dialog: MatDialog,
148151
private _domSanitizer: DomSanitizer,
149152
private _elementRef: ElementRef,
@@ -190,6 +193,8 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
190193
this._setupViewer();
191194
}
192195
if (changes['images']) {
196+
this._getOriginalFilename();
197+
193198
this._openImages();
194199
this._unhighlightAllRegions();
195200
// --> TODO: check if this is necessary or could be handled below
@@ -220,7 +225,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
220225

221226
/**
222227
* renders all ReadStillImageFileValues to be found in [[this.images]].
223-
* (Although this.images is a Angular Input property, the built-in change detection of Angular does not detect changes in complex objects or arrays, only reassignment of objects/arrays.
228+
* (Although this.images is an Angular Input property, the built-in change detection of Angular does not detect changes in complex objects or arrays, only reassignment of objects/arrays.
224229
* Use this method if additional ReadStillImageFileValues were added to this.images after creation/assignment of the this.images array.)
225230
*/
226231
updateImages() {
@@ -232,7 +237,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
232237

233238
/**
234239
* renders all regions to be found in [[this.images]].
235-
* (Although this.images is a Angular Input property, the built-in change detection of Angular does not detect changes in complex objects or arrays, only reassignment of objects/arrays.
240+
* (Although this.images is an Angular Input property, the built-in change detection of Angular does not detect changes in complex objects or arrays, only reassignment of objects/arrays.
236241
* Use this method if additional regions were added to the resources.images)
237242
*/
238243
updateRegions() {
@@ -360,6 +365,32 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
360365
this._notification.openSnackBar(message);
361366
}
362367

368+
async downloadStillImage(url: string) {
369+
try {
370+
const res = await this._http.get(url, { responseType: 'blob' }).toPromise();
371+
this.downloadFile(res);
372+
} catch (e) {
373+
this._errorHandler.showMessage(e);
374+
}
375+
}
376+
377+
downloadFile(data) {
378+
const url = window.URL.createObjectURL(data);
379+
const e = document.createElement('a');
380+
e.href = url;
381+
382+
// set filename
383+
if (this.originalFilename === undefined) {
384+
e.download = url.substr(url.lastIndexOf('/') + 1);
385+
} else {
386+
e.download = this.originalFilename;
387+
}
388+
389+
document.body.appendChild(e);
390+
e.click();
391+
document.body.removeChild(e);
392+
}
393+
363394
openReplaceFileDialog() {
364395
const propId = this.parentResource.properties[Constants.HasStillImageFileValue][0].id;
365396

@@ -384,11 +415,31 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
384415
});
385416
}
386417

418+
openImageInNewTab(url: string) {
419+
window.open(url, '_blank');
420+
}
421+
387422
openPage(p: number) {
388423
this.regionDrawMode = false;
389424
this.goToPage.emit(p);
390425
}
391426

427+
private _getOriginalFilename() {
428+
const requestOptions = {
429+
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
430+
withCredentials: true
431+
};
432+
433+
const index = this.images[0].fileValue.fileUrl.indexOf(this.images[0].fileValue.filename);
434+
const pathToJson = this.images[0].fileValue.fileUrl.substring(0, index + this.images[0].fileValue.filename.length) + '/knora.json';
435+
436+
this._http.get(pathToJson, requestOptions).subscribe(
437+
res => {
438+
this.originalFilename = res['originalFilename'];
439+
}
440+
);
441+
}
442+
392443
private _replaceFile(file: UpdateFileValue) {
393444
const updateRes = new UpdateResource();
394445
updateRes.id = this.parentResource.id;
@@ -403,6 +454,8 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
403454
this._valueOperationEventService.emit(
404455
new EmitEvent(Events.FileValueUpdated, new UpdatedFileEventValue(
405456
res2.properties[Constants.HasStillImageFileValue][0])));
457+
458+
this._getOriginalFilename();
406459
},
407460
(error: ApiResponseError) => {
408461
this._errorHandler.showMessage(error);
@@ -415,6 +468,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
415468
* @param startPoint the start point of the drawing
416469
* @param endPoint the end point of the drawing
417470
* @param imageSize the image size for calculations
471+
* @param overlay the overlay element that represents the region
418472
*/
419473
private _openRegionDialog(startPoint: Point2D, endPoint: Point2D, imageSize: Point2D, overlay: Element): void {
420474
const dialogConfig: MatDialogConfig = {
@@ -708,7 +762,8 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
708762
* @param geometry - the geometry describing the ROI
709763
* @param aspectRatio - the aspectRatio (h/w) of the image on which the geometry should be placed
710764
* @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the geometry should be placed
711-
* @param toolTip - the tooltip which should be displayed on mousehover of the svg element
765+
* @param regionLabel - the label of the region
766+
* @param regionComment - the comment of the region
712767
*/
713768
private _createSVGOverlay(regionIri: string, geometry: RegionGeometry, aspectRatio: number, xOffset: number, regionLabel: string, regionComment: string): void {
714769
const lineColor = geometry.lineColor;

‎src/app/workspace/resource/resource.component.html‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
[resourceIri]="incomingResource ? incomingResource.res.id : resource.res.id"
1414
[project]="resource.res.attachedToProject"
1515
[currentTab]="selectedTabLabel"
16-
[parentResource]="resource.res"
16+
[parentResource]="incomingResource ? incomingResource.res : resource.res"
1717
[activateRegion]="selectedRegion"
1818
(loaded)="representationLoaded($event)"
1919
(goToPage)="compoundNavigation($event)"

0 commit comments

Comments
 (0)