Merge pull request #1847 from mspalti/iiif-multi-fix

Bug fix for multiple IIIF image property
This commit is contained in:
Tim Donohue
2022-09-28 17:02:29 -05:00
committed by GitHub
3 changed files with 78 additions and 31 deletions

View File

@@ -12,6 +12,7 @@ import { createPaginatedList } from '../../shared/testing/utils.test';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { MiradorViewerService } from './mirador-viewer.service'; import { MiradorViewerService } from './mirador-viewer.service';
import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowService } from '../../shared/host-window.service';
import { BundleDataService } from '../../core/data/bundle-data.service';
function getItem(metadata: MetadataMap) { function getItem(metadata: MetadataMap) {
@@ -46,6 +47,7 @@ describe('MiradorViewerComponent with search', () => {
declarations: [MiradorViewerComponent], declarations: [MiradorViewerComponent],
providers: [ providers: [
{ provide: BitstreamDataService, useValue: {} }, { provide: BitstreamDataService, useValue: {} },
{ provide: BundleDataService, useValue: {} },
{ provide: HostWindowService, useValue: mockHostWindowService } { provide: HostWindowService, useValue: mockHostWindowService }
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
@@ -108,6 +110,7 @@ describe('MiradorViewerComponent with multiple images', () => {
declarations: [MiradorViewerComponent], declarations: [MiradorViewerComponent],
providers: [ providers: [
{ provide: BitstreamDataService, useValue: {} }, { provide: BitstreamDataService, useValue: {} },
{ provide: BundleDataService, useValue: {} },
{ provide: HostWindowService, useValue: mockHostWindowService } { provide: HostWindowService, useValue: mockHostWindowService }
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
@@ -167,6 +170,7 @@ describe('MiradorViewerComponent with a single image', () => {
declarations: [MiradorViewerComponent], declarations: [MiradorViewerComponent],
providers: [ providers: [
{ provide: BitstreamDataService, useValue: {} }, { provide: BitstreamDataService, useValue: {} },
{ provide: BundleDataService, useValue: {} },
{ provide: HostWindowService, useValue: mockHostWindowService } { provide: HostWindowService, useValue: mockHostWindowService }
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
@@ -225,6 +229,7 @@ describe('MiradorViewerComponent in development mode', () => {
set: { set: {
providers: [ providers: [
{ provide: MiradorViewerService, useValue: viewerService }, { provide: MiradorViewerService, useValue: viewerService },
{ provide: BundleDataService, useValue: {} },
{ provide: HostWindowService, useValue: mockHostWindowService } { provide: HostWindowService, useValue: mockHostWindowService }
] ]
} }

View File

@@ -8,6 +8,7 @@ import { map, take } from 'rxjs/operators';
import { isPlatformBrowser } from '@angular/common'; import { isPlatformBrowser } from '@angular/common';
import { MiradorViewerService } from './mirador-viewer.service'; import { MiradorViewerService } from './mirador-viewer.service';
import { HostWindowService, WidthCategory } from '../../shared/host-window.service'; import { HostWindowService, WidthCategory } from '../../shared/host-window.service';
import { BundleDataService } from '../../core/data/bundle-data.service';
@Component({ @Component({
selector: 'ds-mirador-viewer', selector: 'ds-mirador-viewer',
@@ -55,6 +56,7 @@ export class MiradorViewerComponent implements OnInit {
constructor(private sanitizer: DomSanitizer, constructor(private sanitizer: DomSanitizer,
private viewerService: MiradorViewerService, private viewerService: MiradorViewerService,
private bitstreamDataService: BitstreamDataService, private bitstreamDataService: BitstreamDataService,
private bundleDataService: BundleDataService,
private hostWindowService: HostWindowService, private hostWindowService: HostWindowService,
@Inject(PLATFORM_ID) private platformId: any) { @Inject(PLATFORM_ID) private platformId: any) {
} }
@@ -107,10 +109,10 @@ export class MiradorViewerComponent implements OnInit {
this.notMobile = !(category === WidthCategory.XS || category === WidthCategory.SM); this.notMobile = !(category === WidthCategory.XS || category === WidthCategory.SM);
}); });
// We need to set the multi property to true if the // Set the multi property. The default mirador configuration adds a right
// item is searchable or when the ORIGINAL bundle contains more // thumbnail navigation panel to the viewer when multi is 'true'.
// than 1 image. (The multi property determines whether the
// Mirador side thumbnail navigation panel is shown.) // Set the multi property to 'true' if the item is searchable.
if (this.searchable) { if (this.searchable) {
this.multi = true; this.multi = true;
const observable = of(''); const observable = of('');
@@ -120,8 +122,12 @@ export class MiradorViewerComponent implements OnInit {
}) })
); );
} else { } else {
// Sets the multi value based on the image count. // Set the multi property based on the image count in IIIF-eligible bundles.
this.iframeViewerUrl = this.viewerService.getImageCount(this.object, this.bitstreamDataService).pipe( // Any count greater than 1 sets the value to 'true'.
this.iframeViewerUrl = this.viewerService.getImageCount(
this.object,
this.bitstreamDataService,
this.bundleDataService).pipe(
map(c => { map(c => {
if (c > 1) { if (c > 1) {
this.multi = true; this.multi = true;

View File

@@ -1,14 +1,18 @@
import { Injectable, isDevMode } from '@angular/core'; import { Injectable, isDevMode } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Item } from '../../core/shared/item.model'; import { Item } from '../../core/shared/item.model';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import {
import { last, map, switchMap } from 'rxjs/operators'; getFirstCompletedRemoteData,
} from '../../core/shared/operators';
import { filter, last, map, mergeMap, switchMap } from 'rxjs/operators';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginatedList } from '../../core/data/paginated-list.model';
import { Bitstream } from '../../core/shared/bitstream.model'; import { Bitstream } from '../../core/shared/bitstream.model';
import { BitstreamFormat } from '../../core/shared/bitstream-format.model'; import { BitstreamFormat } from '../../core/shared/bitstream-format.model';
import { BitstreamDataService } from '../../core/data/bitstream-data.service'; import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { followLink, FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { Bundle } from '../../core/shared/bundle.model';
import { BundleDataService } from '../../core/data/bundle-data.service';
@Injectable() @Injectable()
export class MiradorViewerService { export class MiradorViewerService {
@@ -26,32 +30,64 @@ export class MiradorViewerService {
} }
/** /**
* Returns observable of the number of images in the ORIGINAL bundle * Returns observable of the number of images found in eligible IIIF bundles. Checks
* the mimetype of the first 5 bitstreams in each bundle.
* @param item * @param item
* @param bitstreamDataService * @param bitstreamDataService
* @param bundleDataService
* @returns the total image count
*/ */
getImageCount(item: Item, bitstreamDataService: BitstreamDataService): Observable<number> { getImageCount(item: Item, bitstreamDataService: BitstreamDataService, bundleDataService: BundleDataService):
let count = 0; Observable<number> {
return bitstreamDataService.findAllByItemAndBundleName(item, 'ORIGINAL', { let count = 0;
currentPage: 1, return bundleDataService.findAllByItem(item).pipe(
elementsPerPage: 10 getFirstCompletedRemoteData(),
}, true, true, ...this.LINKS_TO_FOLLOW) map((bundlesRD: RemoteData<PaginatedList<Bundle>>) => {
.pipe( return bundlesRD.payload;
getFirstCompletedRemoteData(), }),
map((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => bitstreamsRD.payload), map((paginatedList: PaginatedList<Bundle>) => paginatedList.page),
map((paginatedList: PaginatedList<Bitstream>) => paginatedList.page), switchMap((bundles: Bundle[]) => bundles),
switchMap((bitstreams: Bitstream[]) => bitstreams), filter((b: Bundle) => this.isIiifBundle(b.name)),
switchMap((bitstream: Bitstream) => bitstream.format.pipe( mergeMap((bundle: Bundle) => {
getFirstSucceededRemoteDataPayload(), return bitstreamDataService.findAllByItemAndBundleName(item, bundle.name, {
map((format: BitstreamFormat) => format) currentPage: 1,
)), elementsPerPage: 5
map((format: BitstreamFormat) => { }, true, true, ...this.LINKS_TO_FOLLOW).pipe(
if (format.mimetype.includes('image')) { getFirstCompletedRemoteData(),
count++; map((bitstreamsRD: RemoteData<PaginatedList<Bitstream>>) => {
} return bitstreamsRD.payload;
return count; }),
}), map((paginatedList: PaginatedList<Bitstream>) => paginatedList.page),
last() switchMap((bitstreams: Bitstream[]) => bitstreams),
switchMap((bitstream: Bitstream) => bitstream.format.pipe(
getFirstCompletedRemoteData(),
map((formatRD: RemoteData<BitstreamFormat>) => {
return formatRD.payload;
}),
map((format: BitstreamFormat) => {
if (format.mimetype.includes('image')) {
count++;
}
return count;
}),
)
)
);
}),
last()
); );
} }
isIiifBundle(bundleName: string): boolean {
return !(
bundleName === 'OtherContent' ||
bundleName === 'LICENSE' ||
bundleName === 'THUMBNAIL' ||
bundleName === 'TEXT' ||
bundleName === 'METADATA' ||
bundleName === 'CC-LICENSE' ||
bundleName === 'BRANDED_PREVIEW'
);
}
} }