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

Angular support for IIIF and Mirador #1079

Merged
merged 67 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
156e9a0
Added mirador deps and scripts to package.json
mspalti Mar 27, 2021
a56ebad
Added webpack configuration for mirador.
mspalti Mar 27, 2021
7e1c441
Added mirador route to express server.
mspalti Mar 27, 2021
c6bc21f
Added iiif entity group.
mspalti Mar 27, 2021
27afd41
Added FileSectionComponent to shared module.
mspalti Mar 27, 2021
6081dab
Added mirador files for webpack build
mspalti Mar 27, 2021
30ff10e
Added iiif entity module to parent item module.
mspalti Mar 27, 2021
2ec1096
Added iiif labels to en.json5
mspalti Mar 27, 2021
f377347
Updates to iiif list elements.
mspalti Mar 27, 2021
aa27dda
yarn.lock
mspalti Mar 27, 2021
3b5df5d
Added mirador deps and scripts to package.json
mspalti Mar 27, 2021
c02983c
Added webpack configuration for mirador.
mspalti Mar 27, 2021
42802bd
Added mirador route to express server.
mspalti Mar 27, 2021
23e7675
Added iiif entity group.
mspalti Mar 27, 2021
71851fe
Added FileSectionComponent to shared module.
mspalti Mar 27, 2021
2d694a8
Added mirador files for webpack build
mspalti Mar 27, 2021
187ae50
Added iiif entity module to parent item module.
mspalti Mar 27, 2021
45c6f4e
Added iiif labels to en.json5
mspalti Mar 27, 2021
5a8f38a
Updates to iiif list elements.
mspalti Mar 27, 2021
0330b3e
yarn.lock
mspalti Mar 27, 2021
f52ed2a
Updated iiif component tests.
mspalti Mar 30, 2021
c0aac8a
Added mobile breakpoint for mirador viewer configuration.
mspalti Mar 30, 2021
e4a51af
Merge branch 'iiif-mirador' of https://github.com/mspalti/dspace-angu…
mspalti Mar 30, 2021
0250b18
Changed property name
mspalti Mar 30, 2021
97e8c9b
Added title to the mirador iframe.
mspalti Mar 30, 2021
3744abb
Allow canvas side panel on all views.
mspalti Apr 1, 2021
ce11ae2
Merge branch 'main' into iiif-mirador
mspalti Apr 1, 2021
3bc7739
Minor fix in module.
mspalti Apr 1, 2021
247ae73
Merge branch 'main' into iiif-mirador
mspalti Apr 2, 2021
6f55225
Removed unused variable.
mspalti Apr 2, 2021
c37b315
Merge remote-tracking branch 'upstream/main' into iiif-mirador
mspalti Apr 2, 2021
2561d54
Added comments
mspalti Apr 16, 2021
4791cce
Merge branch 'main' into iiif-mirador
mspalti Apr 16, 2021
98571a4
Merge remote-tracking branch 'origin/iiif-mirador' into iiif-mirador
mspalti Apr 20, 2021
3dacbe5
Merge branch 'main' into iiif-mirador
mspalti May 16, 2021
3bd8e35
Added description field to iiif item view.
mspalti May 18, 2021
da2bbce
Merge branch 'main' into iiif-mirador
mspalti May 18, 2021
9c41cd7
Merge branch 'main' into iiif-mirador
mspalti Jul 11, 2021
de352a8
Retrieve thumbnail from remote data object.
mspalti Jul 12, 2021
20477f0
Merge branch 'main' into iiif-mirador
mspalti Jul 30, 2021
9cc3351
Fixed item page component paths.
mspalti Jul 30, 2021
6019a21
Removed unused import.
mspalti Jul 31, 2021
fb0d51c
Improved performance of mirador component by avoiding IIIF bundle bit…
mspalti Aug 19, 2021
668a08b
Updated the iiif endpoint that is passed to the viewer.
mspalti Aug 25, 2021
ded5e29
Merge branch 'main' into iiif-mirador
mspalti Sep 17, 2021
3bf9c5f
Starting update for iiif using non-entity metadata.
mspalti Sep 28, 2021
abb733b
Merge branch 'main' into iiif-mirador
mspalti Sep 28, 2021
ec0e8c7
Completed the initial embed of mirador viewer.
mspalti Sep 28, 2021
670a0b8
Completed work on angular tests.
mspalti Sep 29, 2021
d4a6ed6
Mirador component multi-view based on count of image files in the def…
mspalti Sep 30, 2021
baa94ca
Fixed proble with mirador iframe url for a single image.
mspalti Oct 8, 2021
681b10e
Removed unused import.
mspalti Oct 8, 2021
2d11136
Viewer updates.
mspalti Oct 12, 2021
538e3cb
Mirador component update.
mspalti Oct 12, 2021
a875f68
Merge branch 'iiif-mirador' of https://github.com/mspalti/dspace-angu…
mspalti Oct 12, 2021
b738065
Updated service mock for tests.
mspalti Oct 14, 2021
8c05c6d
Merge branch 'main' into iiif-mirador
mspalti Oct 14, 2021
3a1abe8
Removed unused import.
mspalti Oct 14, 2021
24a6a4c
Checking for dev mode to prevent viewer embed.
mspalti Oct 14, 2021
80a9770
More neutral primary color in mirador viewer configuration.
mspalti Oct 14, 2021
073296e
Mirador component update and spec.
mspalti Oct 16, 2021
6a2d856
Removed unused import.
mspalti Oct 16, 2021
a3511ab
Merge branch 'iiif-mirador' of https://github.com/mspalti/dspace-angu…
mspalti Oct 18, 2021
8ac122b
Updated viewport size check.
mspalti Oct 18, 2021
b0fcdf6
Updated test.
mspalti Oct 18, 2021
2b19b16
Merge branch 'main' into iiif-mirador
mspalti Oct 20, 2021
2c60cc4
Updated versioned-item to provide route service in super constructor …
mspalti Oct 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts",
"start:dev": "npm-run-all --parallel config:dev:watch serve",
"start:prod": "yarn run build:prod && yarn run serve:ssr",
"start:mirador:prod": "yarn run build:mirador && yarn run start:prod",
"analyze": "webpack-bundle-analyzer dist/browser/stats.json",
"build": "ng build",
"build:stats": "ng build --stats-json",
Expand All @@ -44,6 +45,7 @@
"clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node",
"clean:env": "rimraf src/environments/environment.ts",
"sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts",
"build:mirador": "webpack --config webpack/webpack.mirador.config.ts",
"merge-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts",
"postinstall": "ngcc",
"cypress:open": "cypress open",
Expand Down Expand Up @@ -106,6 +108,9 @@
"jsonschema": "1.4.0",
"jwt-decode": "^3.1.2",
"klaro": "^0.7.10",
"mirador": "^3.0.0",
"mirador-dl-plugin": "^0.13.0",
"mirador-share-plugin": "^0.10.0",
"moment": "^2.29.1",
"morgan": "^1.10.0",
"ng-mocks": "10.5.4",
Expand All @@ -118,6 +123,8 @@
"nouislider": "^14.6.3",
"pem": "1.14.4",
"postcss-cli": "^8.3.0",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^6.6.3",
"rxjs-spy": "^7.5.3",
Expand Down Expand Up @@ -157,6 +164,7 @@
"deep-freeze": "0.0.1",
"dotenv": "^8.2.0",
"fork-ts-checker-webpack-plugin": "^6.0.3",
"html-loader": "^1.3.2",
"html-webpack-plugin": "^4.5.0",
"http-proxy-middleware": "^1.0.5",
"jasmine-core": "^3.6.0",
Expand Down
6 changes: 6 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import { UIServerConfig } from './src/config/ui-server-config.interface';
* Set path for the browser application's dist folder
*/
const DIST_FOLDER = join(process.cwd(), 'dist/browser');
// Set path fir IIIF viewer.
const IIIF_VIEWER = join(process.cwd(), 'dist/iiif');

const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : 'index';

Expand Down Expand Up @@ -135,6 +137,10 @@ export function app() {
* Serve static resources (images, i18n messages, …)
*/
server.get('*.*', cacheControl, express.static(DIST_FOLDER, { index: false }));
/*
* Fallthrough to the IIIF viewer (must be included in the build).
*/
server.use('/iiif', express.static(IIIF_VIEWER, {index:false}));

// Register the ngApp callback function to handle incoming requests
server.get('*', ngApp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-dat
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { JournalComponent } from './journal.component';
import { RouteService } from '../../../../core/services/route.service';

let comp: JournalComponent;
let fixture: ComponentFixture<JournalComponent>;
Expand Down Expand Up @@ -86,6 +87,7 @@ describe('JournalComponent', () => {
{ provide: NotificationsService, useValue: {} },
{ provide: DefaultChangeAnalyzer, useValue: {} },
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
{ provide: RouteService, useValue: {} }
],

schemas: [NO_ERRORS_SCHEMA]
Expand Down
8 changes: 4 additions & 4 deletions src/app/item-page/item-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { ItemPageAbstractFieldComponent } from './simple/field-components/specif
import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component';
import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
import { ItemPageFieldComponent } from './simple/field-components/specific-field/item-page-field.component';
import { FileSectionComponent } from './simple/field-components/file-section/file-section.component';
import { CollectionsComponent } from './field-components/collections/collections.component';
import { FullItemPageComponent } from './full/full-item-page.component';
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
Expand All @@ -31,6 +30,7 @@ import { MediaViewerComponent } from './media-viewer/media-viewer.component';
import { MediaViewerVideoComponent } from './media-viewer/media-viewer-video/media-viewer-video.component';
import { MediaViewerImageComponent } from './media-viewer/media-viewer-image/media-viewer-image.component';
import { NgxGalleryModule } from '@kolkov/ngx-gallery';
import { MiradorViewerComponent } from './mirador-viewer/mirador-viewer.component';

const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
Expand All @@ -50,7 +50,6 @@ const DECLARATIONS = [
ItemPageUriFieldComponent,
ItemPageTitleFieldComponent,
ItemPageFieldComponent,
FileSectionComponent,
CollectionsComponent,
FullFileSectionComponent,
PublicationComponent,
Expand All @@ -60,7 +59,8 @@ const DECLARATIONS = [
AbstractIncrementalListComponent,
MediaViewerComponent,
MediaViewerVideoComponent,
MediaViewerImageComponent
MediaViewerImageComponent,
MiradorViewerComponent
];

@NgModule({
Expand All @@ -72,7 +72,7 @@ const DECLARATIONS = [
StatisticsModule.forRoot(),
JournalEntitiesModule.withEntryComponents(),
ResearchEntitiesModule.withEntryComponents(),
NgxGalleryModule,
NgxGalleryModule,
],
declarations: [
...DECLARATIONS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p class="full-text-op">{{'iiifviewer.fullscreen.notice' | translate}}</p>
<iframe title="Mirador Viewer" allowtransparency="true" [src]="iframeViewerUrl | async" id="mirador-viewer"></iframe>

13 changes: 13 additions & 0 deletions src/app/item-page/mirador-viewer/mirador-viewer.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#mirador-viewer {
border: 1px solid #cccccc;
height: 660px;
width: 100%
}
.full-text-op {
text-align: right;
color: #333333;
font-size: 0.8em;
}
p.full-text-op {
margin-bottom: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// tests needed.
154 changes: 154 additions & 0 deletions src/app/item-page/mirador-viewer/mirador-viewer.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, PLATFORM_ID } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { Item } from '../../core/shared/item.model';
import { environment } from '../../../environments/environment';
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload
} from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model';
import { Bitstream } from '../../core/shared/bitstream.model';
import { Observable } from 'rxjs/internal/Observable';
import { last, map, switchMap} from 'rxjs/operators';
import { of } from 'rxjs';
import { isPlatformBrowser } from '@angular/common';
import { BitstreamFormat } from '../../core/shared/bitstream-format.model';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';

@Component({
selector: 'ds-mirador-viewer',
styleUrls: ['./mirador-viewer.component.scss'],
templateUrl: './mirador-viewer.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MiradorViewerComponent implements OnInit {

@Input() item: Item;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add typedoc here and for the following properties


/**
* A previous dspace search query.
*/
@Input() query: string;

/**
* True if searchable.
*/
@Input() searchable: boolean;

/**
* The url for the iframe.
*/
iframeViewerUrl: Observable<SafeResourceUrl>;

/**
* Sets the viewer to show or hide thumbnail side navigation menu.
*/
multi = false;

/**
* Hides the thumbnail navigation menu on smaller viewports.
*/
notMobile = false;

LINKS_TO_FOLLOW: FollowLinkConfig<Bitstream>[] = [
followLink('format'),
];

constructor(private sanitizer: DomSanitizer,
private bitstreamDataService: BitstreamDataService,
@Inject(PLATFORM_ID) private platformId: any) {
}

/**
* Creates the url for the Mirador iframe. Adds parameters for the displaying the search panel, query results,
* or multi-page thumbnail navigation.
*/
setURL() {
// The path to the REST manifest endpoint.
const manifestApiEndpoint = encodeURIComponent(environment.rest.baseUrl + '/iiif/'
+ this.item.id + '/manifest');
// The Express path to Mirador viewer.
let viewerPath = '/iiif/mirador/index.html?manifest=' + manifestApiEndpoint;
if (this.searchable) {
// Tell the viewer add search to menu.
viewerPath += '&searchable=' + this.searchable;
}
if (this.query) {
// Tell the viewer to execute a search for the query term.
viewerPath += '&query=' + this.query;
}
if (this.multi) {
// Tell the viewer to add thumbnail navigation. If searchable, thumbnail navigation is added by default.
viewerPath += '&multi=' + this.multi;
}
if (this.notMobile) {
viewerPath += '&notMobile=true';
}
// TODO: review whether the item.id should be sanitized. The query term probably should be.
return this.sanitizer.bypassSecurityTrustResourceUrl(viewerPath);
}

ngOnInit(): void {
/**
* Initializes the iframe url observable.
*/
if (isPlatformBrowser(this.platformId)) {
// The notMobile property affects only the thumbnail navigation
// menu by hiding it for smaller viewports. This will not be
// responsive to resizing.
if (window.innerWidth > 768) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use the HostWindowService.isMd to make this check.

However it'd be nice if the player is responsive to resizing in order to have the mobile version on small windows size when I start from a large window view and viceversa.

Copy link
Member Author

@mspalti mspalti Oct 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atarix83 , I agree with you on resizing as a general rule but this addresses a slightly different problem. It configures the initial state of the Mirador viewer inside the iframe and has no effect after the viewer is loaded. It's here because I noticed that the viewer requires the user to hide panels in the UI when there's a configuration change. The mobile check exists to get them to a good place at the initial load. Otherwise mobile devices always start with panels that need to be manually closed. I can switch to HostWIndowService if you recommend it but checking with you first since this seems like is a situation that may not require the observable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I concur with @atarix83 here: it would be better to use hostWindowService, if only to not to have to hardcode that screen width in multiple places.

The observable shouldn't be a problem. It's an ngrx store observable, so it'll emit right away (synchronously). You can replace the if you have with something like:

    this.hostWindowService.widthCategory
      .pipe(take(1))
      .subscribe((category: WidthCategory) => {
        this.notMobile = !(category === WidthCategory.XS || category === WidthCategory.SM);
    });

And in the line below that subscribe, you can be sure this.notMobile will be set.
That way you don't have to turn notMobile into an observable too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use hostWindowService.

this.notMobile = true;
}
// We need to set the multi property to true if the
// item is searchable or the ORIGINAL bundle contains more
// than 1 image bitstream. The multi property determine whether the
// Mirador side navigation panel is shown.
if (this.searchable) {
// If it's searchable set multi to true.
const observable = of({multi: true});
this.iframeViewerUrl = observable.pipe(
map((val) => {
this.multi = val.multi;
return this.setURL();
})
);
} else {
// Gets the first 10 items in the bundle and counts the number of images. Emits the final count.
let count = 0;
const imageCount$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', {
currentPage: 1,
elementsPerPage: 10
}, true, true, ...this.LINKS_TO_FOLLOW)
.pipe(
getFirstCompletedRemoteData(),
map((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => bitstreamsRD.payload),
map((paginatedList: PaginatedList<Bitstream>) => paginatedList.page),
switchMap((bitstreams: Bitstream[]) => bitstreams),
switchMap((bitstream: Bitstream) => bitstream.format.pipe(
getFirstSucceededRemoteDataPayload(),
map((format: BitstreamFormat) => format)
)),
map((format: BitstreamFormat) => {
if (format.mimetype.includes('image')) {
count++;
}
return count;
}),
last()
);

// Sets the multi value based on the image count and then sets the iframe url.
this.iframeViewerUrl = imageCount$.pipe(
map(c => {
if (count > 1) {
this.multi = true;
}
return this.setURL();
})
);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
<div class="row" *ngIf="iiifEnabled">
<div class="col-12">
<ds-mirador-viewer id="iiif-viewer"
[item]="object"
[searchable]="iiifSearchEnabled"
[query]="iiifQuery$ | async">
</ds-mirador-viewer>
</div>
</div>
<div class="d-flex flex-row">
<h2 class="item-page-title-field mr-auto">
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
Expand Down
Loading