Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(still-image): new still-image viewer (DEV-1150) #792

Merged
merged 11 commits into from Aug 11, 2022
Expand Up @@ -35,13 +35,19 @@
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #more="matMenu">
<a mat-menu-item [href]="iiifUrl" target="_blank">
Open file in new tab
</a>
<button mat-menu-item (click)="openImageInNewTab(iiifUrl)">
Open IIIF file in new tab
</button>
<button mat-menu-item [cdkCopyToClipboard]="iiifUrl"
(click)="openSnackBar('IIIF URL copied to clipboard!')">
Copy IIIF URL to clipboard
</button>
<button mat-menu-item (click)="downloadStillImage(images[0].fileValue.fileUrl)">
Download file
</button>
<button mat-menu-item (click)="openReplaceFileDialog()">
Replace file
</button>
</mat-menu>
<!-- empty placeholder to simulate two buttons; this helps to have the zoom buttons centered with fill-remaining-space setup -->
<span class="empty-space"></span>
Expand Down Expand Up @@ -88,16 +94,12 @@

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

<!-- action buttons: create annotation/region, replace file, fullscreen -->
<!-- action buttons: create annotation/region, fullscreen -->
<span>
<button mat-icon-button id="DSP_OSD_DRAW_REGION" matTooltip="Draw Region" [disabled]="failedToLoad"
(click)="drawButtonClicked()" [class.active]="regionDrawMode">
<mat-icon svgIcon="draw_region_icon"></mat-icon>
</button>
<button mat-icon-button id="DSP_OSD_REPLACE_IMAGE" class="replace-image" matTooltip="Replace image"
(click)="openReplaceFileDialog()">
<mat-icon>cloud_upload</mat-icon>
</button>
<button mat-icon-button id="DSP_OSD_FULL_PAGE" matTooltip="Open in fullscreen" [disabled]="failedToLoad">
<mat-icon>fullscreen</mat-icon>
</button>
Expand Down
Expand Up @@ -21,6 +21,7 @@
color: $bright;
background-color: $dark;
height: calc(100% - 64px);
border-radius: 8px 8px 0px 0px;

&.drawing {
cursor: crosshair;
Expand Down Expand Up @@ -110,6 +111,14 @@
z-index: 1000;
}

/*
Openseadragon styling
*/

::ng-deep .openseadragon-container {
border-radius: 8px 8px 0px 0px;
}

/*
Overlay styling
*/
Expand Down
Expand Up @@ -2,9 +2,9 @@ import { CdkCopyToClipboard } from '@angular/cdk/clipboard';
import { OverlayContainer } from '@angular/cdk/overlay';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HttpClientModule } from '@angular/common/http';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
Expand Down Expand Up @@ -124,7 +124,7 @@ describe('StillImageComponent', () => {
let rootLoader: HarnessLoader;
let overlayContainer: OverlayContainer;

beforeEach(waitForAsync(() => {
beforeEach(() => {

const adminSpyObj = {
v2: {
Expand All @@ -140,7 +140,7 @@ describe('StillImageComponent', () => {
],
imports: [
BrowserAnimationsModule,
HttpClientModule,
HttpClientTestingModule,
MatDialogModule,
MatIconModule,
MatMenuModule,
Expand All @@ -164,7 +164,7 @@ describe('StillImageComponent', () => {
]
})
.compileComponents();
}));
});

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

overlayContainer = TestBed.inject(OverlayContainer);
rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostFixture);
});

it('should create', () => {
expect(testHostComponent).toBeTruthy();
expect(testHostComponent.osdViewerComp).toBeTruthy();
});
Expand Down
Expand Up @@ -14,6 +14,7 @@ import {
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {
ApiResponseError,
Constants,
Expand Down Expand Up @@ -136,6 +137,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit

loading = true;
failedToLoad = false;
originalFilename: string;

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

constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
private readonly _http: HttpClient,
private _dialog: MatDialog,
private _domSanitizer: DomSanitizer,
private _elementRef: ElementRef,
Expand Down Expand Up @@ -190,6 +193,8 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
this._setupViewer();
}
if (changes['images']) {
this._getOriginalFilename();

this._openImages();
this._unhighlightAllRegions();
// --> TODO: check if this is necessary or could be handled below
Expand Down Expand Up @@ -220,7 +225,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit

/**
* renders all ReadStillImageFileValues to be found in [[this.images]].
* (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.
* (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.
* Use this method if additional ReadStillImageFileValues were added to this.images after creation/assignment of the this.images array.)
*/
updateImages() {
Expand All @@ -232,7 +237,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit

/**
* renders all regions to be found in [[this.images]].
* (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.
* (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.
* Use this method if additional regions were added to the resources.images)
*/
updateRegions() {
Expand Down Expand Up @@ -360,6 +365,32 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
this._notification.openSnackBar(message);
}

async downloadStillImage(url: string) {
try {
const res = await this._http.get(url, { responseType: 'blob' }).toPromise();
this.downloadFile(res);
} catch (e) {
this._errorHandler.showMessage(e);
}
}

downloadFile(data) {
const url = window.URL.createObjectURL(data);
const e = document.createElement('a');
e.href = url;

// set filename
if (this.originalFilename === undefined) {
e.download = url.substr(url.lastIndexOf('/') + 1);
} else {
e.download = this.originalFilename;
}

document.body.appendChild(e);
e.click();
document.body.removeChild(e);
}

openReplaceFileDialog() {
const propId = this.parentResource.properties[Constants.HasStillImageFileValue][0].id;

Expand All @@ -384,11 +415,31 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
});
}

openImageInNewTab(url: string) {
window.open(url, '_blank');
}

openPage(p: number) {
this.regionDrawMode = false;
this.goToPage.emit(p);
}

private _getOriginalFilename() {
const requestOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
withCredentials: true
};

const index = this.images[0].fileValue.fileUrl.indexOf(this.images[0].fileValue.filename);
const pathToJson = this.images[0].fileValue.fileUrl.substring(0, index + this.images[0].fileValue.filename.length) + '/knora.json';

this._http.get(pathToJson, requestOptions).subscribe(
res => {
this.originalFilename = res['originalFilename'];
}
);
}

private _replaceFile(file: UpdateFileValue) {
const updateRes = new UpdateResource();
updateRes.id = this.parentResource.id;
Expand All @@ -403,6 +454,8 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
this._valueOperationEventService.emit(
new EmitEvent(Events.FileValueUpdated, new UpdatedFileEventValue(
res2.properties[Constants.HasStillImageFileValue][0])));

this._getOriginalFilename();
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
Expand All @@ -415,6 +468,7 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
* @param startPoint the start point of the drawing
* @param endPoint the end point of the drawing
* @param imageSize the image size for calculations
* @param overlay the overlay element that represents the region
*/
private _openRegionDialog(startPoint: Point2D, endPoint: Point2D, imageSize: Point2D, overlay: Element): void {
const dialogConfig: MatDialogConfig = {
Expand Down Expand Up @@ -708,7 +762,8 @@ export class StillImageComponent implements OnChanges, OnDestroy, AfterViewInit
* @param geometry - the geometry describing the ROI
* @param aspectRatio - the aspectRatio (h/w) of the image on which the geometry should be placed
* @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the geometry should be placed
* @param toolTip - the tooltip which should be displayed on mousehover of the svg element
* @param regionLabel - the label of the region
* @param regionComment - the comment of the region
*/
private _createSVGOverlay(regionIri: string, geometry: RegionGeometry, aspectRatio: number, xOffset: number, regionLabel: string, regionComment: string): void {
const lineColor = geometry.lineColor;
Expand Down
2 changes: 1 addition & 1 deletion src/app/workspace/resource/resource.component.html
Expand Up @@ -13,7 +13,7 @@
[resourceIri]="incomingResource ? incomingResource.res.id : resource.res.id"
[project]="resource.res.attachedToProject"
[currentTab]="selectedTabLabel"
[parentResource]="resource.res"
[parentResource]="incomingResource ? incomingResource.res : resource.res"
[activateRegion]="selectedRegion"
(loaded)="representationLoaded($event)"
(goToPage)="compoundNavigation($event)"
Expand Down