Phase 4: Download Logic Hook
Last updated
describe('useDownloadOptions', () => {
it('should return empty array when no canvas or manifest', () => { ... });
it('should include IIIF image download options', () => { ... });
it('should include canvas image downloads from services', () => { ... });
it('should include canvas rendering downloads (PDFs)', () => { ... });
it('should include manifest-level downloads', () => { ... });
it('should include video/audio downloads', () => { ... });
it('should handle ChoiceBody items correctly', () => { ... });
it('should deduplicate downloads with same id', () => { ... }); // NEW from restricted-images
it('should memoise results and only recalculate when dependencies change', () => { ... });
});
describe('useImageServices', () => {
it('should extract image services from canvas painting', () => { ... });
it('should return empty array when no painting items', () => { ... });
it('should handle undefined currentCanvas', () => { ... });
});export function useDownloadOptions(iiifImageLocation?: DigitalLocation) {
const { work, currentCanvas, transformedManifest } = useItemViewerContext();
const transformedIIIFImage = useTransformedIIIFImage(work);
return useMemo(() => {
// Extract image services
const imageServices = /* ... */;
// Calculate all download options
const iiifImageDownloadOptions = /* ... */;
const canvasImageDownloads = /* ... */;
const canvasDownloadOptions = /* ... */;
const manifestDownloadOptions = /* ... */;
const videoAudioDownloadOptions = /* ... */;
// Combine all options
const allOptions = [
...iiifImageDownloadOptions,
...canvasImageDownloads,
...canvasDownloadOptions,
...manifestDownloadOptions,
...videoAudioDownloadOptions,
];
// Deduplicate by download URL (id)
// Added in restricted-images merge - prevents same file appearing multiple times
// when it's available from multiple sources in the IIIF manifest
return allOptions.filter(
(option, index, self) =>
self.findIndex(o => o.id === option.id) === index
);
}, [currentCanvas, transformedManifest, iiifImageLocation, /* ... */]);
}// In utils/iiif/v3/index.ts (already implemented)
export function getDownloadOptionsFromCanvasRenderingAndSupplementing(
canvas: TransformedCanvas
): DownloadOption[] {
return [...canvas.rendering, ...canvas.supplementing]
.flatMap(item => (isChoiceBody(item) ? item.items : [item]))
.filter((item): item is ContentResource => typeof item !== 'string')
.map(convertToDownloadOption);
}// ADD import:
import { useDownloadOptions } from '@weco/content/hooks/useDownloadOptions';
// DELETE lines 226-291 (all download calculation logic)
// REPLACE with:
const downloadOptions = useDownloadOptions(iiifImageLocation);yarn test useDownloadOptions.test
yarn test ViewerTopBar.refactored.test