Skip to content

Commit

Permalink
feat: thumbnail fallback (#2492)
Browse files Browse the repository at this point in the history
* feat: handle non-HTML images

by just displaying their thumbnail

* fix: handle no resource set initially

* feat: thumbnail fallback

for non-viewable media, e.g. PDFs, isShownAt, TIFFs

and use same for viewable images without IIIF service until the
thumbnail is interacted with

* feat: only load thumbnail 1st for extra large images

* fix: only display XL notification for images

* test: update specs

* fix: no thumb if active anno

* chore: rm unused format prop

* Refactor full image toast into a button and adjust placement

* Fix alignment keyboard nav toast

* test: spec changes to WebResource class

* test: spec forWebResource thumbnail gen

* chore: rm empty file

* test: spec display of thumbnails

* Align default thumbnail color with the pagination thumbnails and set width and height

* Make MediaCardImage for thumbnail linkable

* Make preview thumbnail linkable only when not a viewable image resource

---------

Co-authored-by: Leonie Peters <[email protected]>
  • Loading branch information
rwd and LeoniePeters authored Dec 2, 2024
1 parent 0b7a3df commit 9d363ca
Show file tree
Hide file tree
Showing 17 changed files with 511 additions and 322 deletions.
114 changes: 75 additions & 39 deletions packages/portal/src/components/item/ItemMediaPresentation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,57 +43,57 @@
:provider-url="providerUrl"
/>
<MediaImageViewer
v-else-if="imageTypeResource"
v-else-if="viewableImageResource && !displayThumbnail"
:url="resource.id"
:item-id="itemId"
:width="resource.width"
:height="resource.height"
:format="resource.format"
:service="resource.service"
:thumbnail="thumbnail"
@error="handleImageError"
>
<MediaImageViewerControls
:fullscreen="fullscreen"
@toggleFullscreen="toggleFullscreen"
/>
</MediaImageViewer>
<MediaPDFViewer
v-else-if="resource?.format === 'application/pdf'"
:url="resource.id"
:item-id="itemId"
class="media-viewer-content"
/>
<MediaAudioVisualPlayer
v-else-if="resource?.edm.isPlayableMedia"
v-else-if="resource?.edm?.isPlayableMedia"
:url="resource.id"
:format="resource.format"
:item-id="itemId"
class="media-viewer-content"
/>
<EmbedOEmbed
v-else-if="resource?.edm.isOEmbed"
v-else-if="resource?.edm?.isOEmbed"
:url="resource.id"
class="media-viewer-content"
/>
<MediaImageViewer
v-else-if="resource?.edm.forEdmIsShownAt"
:url="resource.edm.preview.about"
:item-id="itemId"
:width="resource.edm.preview.ebucoreWidth"
:height="resource.edm.preview.ebucoreHeight"
:thumbnail="thumbnail"
/>
<code
v-else
class="media-viewer-content h-50 w-100 p-5"
<template
v-else-if="displayThumbnail"
>
<pre
:style="{ color: 'white', 'overflow-wrap': 'break-word' }"
><!--
-->{{ JSON.stringify(resource?.edm, null, 2) }}
</pre>
</code>
<!-- TODO: mv into own component, e.g. ItemMediaPreview? -->
<MediaCardImage
:offset="page - 1"
data-qa="item media thumbnail"
:media="resource?.edm"
:lazy="false"
:edm-type="edmType"
:linkable="!viewableImageResource"
thumbnail-size="large"
:europeana-identifier="itemId"
@click.native="() => thumbnailInteractedWith = true"
/>
<b-button
v-if="viewableImageResource"
data-qa="item media load button"
class="full-image-button d-inline-flex align-items-center py-2 px-3"
variant="light-flat"
@click="() => thumbnailInteractedWith = true"
>
<span class="icon-click mr-2" />
{{ $t('media.loadFull') }}
</b-button>
</template>
</template>
</div>
<div
Expand Down Expand Up @@ -131,6 +131,7 @@

<script>
import LoadingSpinner from '../generic/LoadingSpinner.vue';
import MediaCardImage from '../media/MediaCardImage.vue';
import useItemMediaPresentation from '@/composables/itemMediaPresentation.js';
export class ItemMediaPresentationError extends Error {
Expand All @@ -152,9 +153,9 @@
ItemMediaThumbnails: () => import('./ItemMediaThumbnails.vue'),
LoadingSpinner,
MediaAudioVisualPlayer: () => import('../media/MediaAudioVisualPlayer.vue'),
MediaCardImage,
MediaImageViewer: () => import('../media/MediaImageViewer.vue'),
MediaImageViewerControls: () => import('../media/MediaImageViewerControls.vue'),
MediaPDFViewer: () => import('../media/MediaPDFViewer.vue')
MediaImageViewerControls: () => import('../media/MediaImageViewerControls.vue')
},
props: {
Expand Down Expand Up @@ -186,6 +187,7 @@
setup() {
const {
activeAnnotation,
fetchPresentation,
hasAnnotations,
hasSearchService,
Expand All @@ -197,6 +199,7 @@
} = useItemMediaPresentation();
return {
activeAnnotation,
fetchPresentation,
hasAnnotations,
hasSearchService,
Expand All @@ -212,7 +215,8 @@
return {
fullscreen: false,
showPages: true,
showSidebar: !!this.$route.hash
showSidebar: !!this.$route.hash,
thumbnailInteractedWith: false
};
},
Expand Down Expand Up @@ -250,6 +254,18 @@
},
computed: {
displayThumbnail() {
if (this.activeAnnotation) {
return false;
} else if (this.viewableImageResource) {
return !this.service && (this.resource?.edm?.imageSize === 'extra_large') && !this.thumbnailInteractedWith;
} else {
return !(
this.resource?.edm?.isPlayableMedia || this.resource?.edm?.isOEmbed
);
}
},
hasManifest() {
return !!this.uri;
},
Expand All @@ -262,20 +278,16 @@
return this.resourceCount >= 2;
},
thumbnail() {
return this.resource.edm.thumbnails?.(this.$nuxt.context)?.large;
},
imageTypeResource() {
return this.resource?.format?.startsWith('image/');
viewableImageResource() {
return this.resource?.edm?.isHTMLImage;
},
addPaginationToolbarMaxWidth() {
return !this.imageTypeResource && this.multiplePages;
return !this.viewableImageResource && this.multiplePages;
},
addSidebarToggleMaxWidth() {
return !this.imageTypeResource && this.sidebarHasContent;
return !this.viewableImageResource && this.sidebarHasContent;
}
},
Expand Down Expand Up @@ -311,6 +323,7 @@
},
selectResource() {
this.thumbnailInteractedWith = false;
this.$emit('select', this.resource);
},
Expand Down Expand Up @@ -468,4 +481,27 @@
flex-grow: 1;
}
}
.full-image-button {
background-color: $black;
color: $white;
border: 1px solid $white;
position: absolute;
bottom: 1rem;
left: 0;
right: 0;
margin: 0 auto;
width: fit-content;
z-index: 1;
}
.icon-click {
font-size: $font-size-large;
line-height: 1;
}
::v-deep .default-thumbnail {
height: 290px;
width: 290px;
}
</style>
8 changes: 4 additions & 4 deletions packages/portal/src/components/item/ItemMediaThumbnail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class="item-media-thumbnail text-lowercase text-decoration-none"
>
<MediaCardImage
:media="resource.edm"
:media="resource"
:lazy="lazy"
:offset="offset"
:edm-type="edmType"
Expand All @@ -23,7 +23,7 @@

<script>
import MediaCardImage from '../media/MediaCardImage.vue';
import EuropeanaMediaResource from '@/utils/europeana/media/Resource.js';
import WebResource from '@/plugins/europeana/edm/WebResource.js';
export default {
name: 'ItemMediaThumbnail',
Expand All @@ -33,7 +33,7 @@
},
props: {
resource: {
type: EuropeanaMediaResource,
type: WebResource,
required: true
},
offset: {
Expand Down Expand Up @@ -62,7 +62,7 @@
return this.offset + 1;
},
mediaTypeIconClass() {
const mediaType = this.resource.edm.edmType || this.edmType;
const mediaType = this.resource.edmType || this.edmType;
return mediaType ? `icon-${mediaType.toLowerCase()}-bold` : '';
},
label() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
:offset="firstRenderedResourceIndex + index"
class="d-flex-inline mr-3 mr-lg-auto"
:class="{ 'selected': index === selectedIndex }"
:resource="resource"
:resource="resource.edm"
:edm-type="edmType"
:lazy="true"
/>
Expand Down
17 changes: 15 additions & 2 deletions packages/portal/src/components/media/MediaCardImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@

<script>
import WebResource from '@/plugins/europeana/edm/WebResource.js';
import {
LARGE_WIDTH as LARGE_THUMBNAIL_WIDTH,
SMALL_WIDTH as SMALL_THUMBNAIL_WIDTH
} from '@/plugins/europeana/thumbnail.js';
export default {
name: 'MediaCardImage',
Expand Down Expand Up @@ -107,7 +111,16 @@
return this.$apis.record.mediaProxyUrl(this.media.about, this.europeanaIdentifier, { disposition: 'inline' });
},
thumbnails() {
return this.media.thumbnails(this.$nuxt.context);
if (this.media.svcsHasService) {
// TODO: assess impact of this outside of new ItemMediaPresentation component
const serviceId = this.media.svcsHasService.id || this.media.svcsHasService.about || this.media.svcsHasService;
return {
large: `${serviceId}/full/${LARGE_THUMBNAIL_WIDTH},/0/default.jpg`,
small: `${serviceId}/full/${SMALL_THUMBNAIL_WIDTH},/0/default.jpg`
};
} else {
return this.$apis.thumbnail.forWebResource(this.media);
}
},
thumbnailSrc() {
return this.thumbnails[this.thumbnailSize];
Expand All @@ -116,7 +129,7 @@
if (!this.media.ebucoreWidth) {
return null;
}
const thumbnailMaxSize = this.thumbnailSize === 'large' ? 400 : 200;
const thumbnailMaxSize = this.thumbnailSize === 'large' ? LARGE_THUMBNAIL_WIDTH : SMALL_THUMBNAIL_WIDTH;
if (this.media.ebucoreWidth < thumbnailMaxSize) {
return this.media.ebucoreWidth;
}
Expand Down
Loading

0 comments on commit 9d363ca

Please sign in to comment.