Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add a fallback for broken provider image (#1493)
Browse files Browse the repository at this point in the history
* Add a fallback for broken provider image
* Fix types and remove unused navigation method
* Add image load error handling
  • Loading branch information
obulat authored Jun 28, 2022
1 parent ac8b5f1 commit 8d25b47
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 125 deletions.
62 changes: 36 additions & 26 deletions src/components/VAllResultsGrid/VImageCellSquare.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
>
<figure
itemprop="image"
itemscope=""
itemscope
itemtype="https://schema.org/ImageObject"
class="aspect-square relative rounded-sm"
>
Expand All @@ -16,11 +16,11 @@
class="w-full h-full object-cover rounded-sm bg-dark-charcoal-10 text-dark-charcoal-10"
loading="lazy"
:alt="image.title"
:src="getImageUrl(image)"
:src="getImageUrl()"
:width="250"
:height="250"
itemprop="thumbnailUrl"
@error="onImageLoadError($event, image)"
@error="onImageLoadError($event)"
/>
<figcaption
class="absolute left-0 bottom-0 invisible group-hover:visible group-focus:visible bg-white p-1 text-dark-charcoal"
Expand All @@ -32,44 +32,54 @@
</VLink>
</template>

<script>
<script lang="ts">
import { defineComponent, PropType } from '@nuxtjs/composition-api'
import type { ImageDetail } from '~/models/media'
import VLink from '~/components/VLink.vue'
import VLicense from '~/components/VLicense/VLicense.vue'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const errorImage = require('~/assets/image_not_available_placeholder.png')
import errorImage from '~/assets/image_not_available_placeholder.png'
const toAbsolutePath = (url, prefix = 'https://') => {
const toAbsolutePath = (url: string, prefix = 'https://') => {
if (url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0) {
return url
}
return `${prefix}${url}`
}
export default {
export default defineComponent({
name: 'VImageCell',
components: { VLink, VLicense },
props: ['image'],
methods: {
getImageUrl(image) {
if (!image) return ''
const url = image.thumbnail || image.url
return toAbsolutePath(url)
props: {
image: {
type: Object as PropType<ImageDetail>,
required: true,
},
getImageForeignUrl(image) {
return toAbsolutePath(image.foreign_landing_url)
},
onImageLoadError(event, image) {
const element = event.target
if (element.src !== image.url) {
element.src = image.url
},
setup(props) {
const getImageUrl = () => {
if (!props.image) return ''
const url = props.image.thumbnail || props.image.url
return toAbsolutePath(url)
}
const getImageForeignUrl = () =>
toAbsolutePath(props.image.foreign_landing_url)
const onImageLoadError = (event: Event) => {
const element = event.target as HTMLImageElement
if (element.src !== props.image.url) {
element.src = props.image.url
} else {
element.src = errorImage
}
},
onFocusLeave(event) {
this.$emit('focus-leave', event)
},
}
return {
getImageUrl,
getImageForeignUrl,
onImageLoadError,
}
},
}
})
</script>
187 changes: 105 additions & 82 deletions src/components/VImageGrid/VImageCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,20 @@
:href="'/image/' + image.id"
class="w-full block group relative overflow-hidden rounded-sm focus:ring-[3px] focus:ring-pink focus:ring-offset-[3px] focus:outline-none bg-dark-charcoal-10 text-dark-charcoal-10"
:aria-label="image.title"
:style="`width: ${containerAspect * widthBasis}px;flex-grow: ${
containerAspect * widthBasis
}`"
@click="onGotoDetailPage($event, image)"
:style="containerStyle"
@keydown.native.shift.tab.exact="$emit('shift-tab', $event)"
>
<figure
class="absolute w-full"
:style="`width: ${imageWidth}%; top: ${imageTop}%; left:${imageLeft}%;`"
>
<figure class="absolute w-full" :style="figureStyle">
<img
ref="img"
loading="lazy"
class="margin-auto block w-full"
:alt="image.title"
:src="getImageUrl(image)"
:src="imageUrl"
:width="imgWidth"
:height="imgHeight"
@load="getImgDimension"
@error="onImageLoadError($event, image)"
@error="onImageLoadError($event)"
/>
<figcaption
class="absolute left-0 bottom-0 invisible group-hover:visible group-focus:visible bg-white p-1 text-dark-charcoal"
Expand All @@ -35,101 +29,130 @@
</VLink>
</template>

<script>
<script lang="ts">
import {
computed,
defineComponent,
PropType,
ref,
} from '@nuxtjs/composition-api'
import type { ImageDetail } from '~/models/media'
import VLicense from '~/components/VLicense/VLicense.vue'
import VLink from '~/components/VLink.vue'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const errorImage = require('~/assets/image_not_available_placeholder.png')
import errorImage from '~/assets/image_not_available_placeholder.png'
const minAspect = 3 / 4
const maxAspect = 16 / 9
const panaromaAspect = 21 / 9
const panoramaAspect = 21 / 9
const minRowWidth = 450
const widthBasis = minRowWidth / maxAspect
const toAbsolutePath = (url, prefix = 'https://') => {
const toAbsolutePath = (url: string, prefix = 'https://') => {
if (url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0) {
return url
}
return `${prefix}${url}`
}
export default {
export default defineComponent({
name: 'VImageCell',
components: { VLicense, VLink },
props: ['image'],
data() {
return {
widthBasis: minRowWidth / maxAspect,
imgHeight: this.image.height || 100,
imgWidth: this.image.width || 100,
}
},
computed: {
imageAspect() {
return this.imgWidth / this.imgHeight
props: {
image: {
type: Object as PropType<ImageDetail>,
required: true,
},
containerAspect() {
if (this.imageAspect > maxAspect) return maxAspect
if (this.imageAspect < minAspect) return minAspect
return this.imageAspect
},
iPadding() {
if (this.imageAspect < minAspect) return (1 / minAspect) * 100
if (this.imageAspect > maxAspect) return (1 / maxAspect) * 100
return (1 / this.imageAspect) * 100
},
imageWidth() {
if (this.imageAspect < maxAspect) return 100
return (this.imageAspect / maxAspect) * 100
},
imageTop() {
if (this.imageAspect > minAspect) return 0
},
setup(props) {
const imgHeight = ref(props.image.height || 100)
const imgWidth = ref(props.image.width || 100)
const imageAspect = computed(() => imgWidth.value / imgHeight.value)
const containerAspect = computed(() => {
if (imageAspect.value > maxAspect) return maxAspect
if (imageAspect.value < minAspect) return minAspect
return imageAspect.value
})
const iPadding = computed(() => {
if (imageAspect.value < minAspect) return (1 / minAspect) * 100
if (imageAspect.value > maxAspect) return (1 / maxAspect) * 100
return (1 / imageAspect.value) * 100
})
const imageWidth = computed(() => {
if (imageAspect.value < maxAspect) return 100
return (imageAspect.value / maxAspect) * 100
})
const imageTop = computed(() => {
if (imageAspect.value > minAspect) return 0
return (
((minAspect - this.imageAspect) /
(this.imageAspect * minAspect * minAspect)) *
((minAspect - imageAspect.value) /
(imageAspect.value * minAspect * minAspect)) *
-50
)
},
imageLeft() {
if (this.imageAspect < maxAspect) return 0
return ((this.imageAspect - maxAspect) / maxAspect) * -50
},
},
methods: {
getImageUrl(image) {
if (!image) {
return ''
}
const url = image.thumbnail || image.url
if (this.imageAspect > panaromaAspect) return toAbsolutePath(url)
})
const imageLeft = computed(() => {
if (imageAspect.value < maxAspect) return 0
return ((imageAspect.value - maxAspect) / maxAspect) * -50
})
const imageUrl = computed(() => {
// TODO: check if we have blurry panorama thumbnails
// fix for blurry panorama thumbnails, introduced in
// https://github.com/cc-archive/cccatalog-frontend/commit/4c9bdac5
if (imageAspect.value > panoramaAspect)
return toAbsolutePath(props.image.url)
const url = props.image.thumbnail || props.image.url
return toAbsolutePath(url)
},
getImageForeignUrl(image) {
return toAbsolutePath(image.foreign_landing_url)
},
onGotoDetailPage(event, image) {
if (!event.metaKey && !event.ctrlKey) {
event.preventDefault()
const detailRoute = this.localeRoute({
name: 'PhotoDetailPage',
params: { id: image.id, location: window.scrollY },
})
this.$router.push(detailRoute)
}
},
onImageLoadError(event, image) {
const element = event.target
if (element.src !== image.url) {
element.src = image.url
})
const getImageForeignUrl = () =>
toAbsolutePath(props.image.foreign_landing_url)
/**
* If the thumbnail fails to load, try replacing it with the original image URL.
* If the original image fails, too, use the error image placeholder.
* @param event - the error event.
*/
const onImageLoadError = (event: Event) => {
const element = event.target as HTMLImageElement
if (element.src !== props.image.url) {
element.src = props.image.url
} else {
element.src = errorImage
}
},
getImgDimension(e) {
this.imgHeight = e.target.naturalHeight
this.imgWidth = e.target.naturalWidth
},
}
const getImgDimension = (event: Event) => {
const element = event.target as HTMLImageElement
imgHeight.value = element.naturalHeight
imgWidth.value = element.naturalWidth
}
const containerStyle = computed(() => {
const containerWidth = containerAspect.value * widthBasis
return `width: ${containerWidth}px;flex-grow: ${containerWidth}`
})
const figureStyle = computed(
() =>
`width: ${imageWidth.value}%; top: ${imageTop.value}%; left:${imageLeft.value}%;`
)
return {
imgHeight,
imgWidth,
containerStyle,
figureStyle,
iPadding,
imageUrl,
getImageForeignUrl,
onImageLoadError,
getImgDimension,
}
},
}
})
</script>
10 changes: 5 additions & 5 deletions src/locales/po-files/openverse.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Openverse \n"
"Report-Msgid-Bugs-To: https://github.com/wordpress/openverse/issues \n"
"POT-Creation-Date: 2022-06-27T05:00:35+00:00\n"
"POT-Creation-Date: 2022-06-28T13:04:47+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
Expand Down Expand Up @@ -467,12 +467,12 @@ msgid "Genre"
msgstr ""

#. Do not translate words between ### ###.
#: src/pages/image/_id.vue:46
#: src/pages/image/_id.vue:47
msgctxt "image-details.creator"
msgid "by ###name###"
msgstr ""

#: src/pages/image/_id.vue:38
#: src/pages/image/_id.vue:39
msgctxt "image-details.weblink"
msgid "Go to image's website"
msgstr ""
Expand Down Expand Up @@ -654,7 +654,7 @@ msgid "Open form"
msgstr ""

#. Do not translate words between ### ###.
#: src/pages/image/_id.vue:51
#: src/pages/image/_id.vue:52
msgctxt "media-details.aria.creator-url"
msgid "author ###name###"
msgstr ""
Expand Down Expand Up @@ -1199,7 +1199,7 @@ msgid "An error occurred"
msgstr ""

#. Do not translate words between ### ###.
#: src/pages/image/_id.vue:193
#: src/pages/image/_id.vue:222
msgctxt "error.image-not-found"
msgid "Couldn't find image with id ###id###"
msgstr ""
Expand Down
Loading

0 comments on commit 8d25b47

Please sign in to comment.