117 lines
2.7 KiB
Vue
117 lines
2.7 KiB
Vue
<template>
|
|
<vTooltip @show="loadContent" @hide="stopTimer">
|
|
<slot />
|
|
<template #popper class="p-0 m-0">
|
|
<span class="icon" v-if="!url"><i class="fas fa-circle-notch fa-spin" /></span>
|
|
<template v-else>
|
|
<div style="min-width: 300px; width: 25vw; height: auto;" class="m-1">
|
|
<div class="is-block" style="word-break: all;" v-if="props.title">
|
|
<span style="font-size: 120%;">{{ props.title }}</span>
|
|
</div>
|
|
<figure class="image is-3by1 is-hidden-mobile">
|
|
<img @load="e => pImg(e)" :src="url" :alt="props.title" @error="clearCache"
|
|
:crossorigin="props.privacy ? 'anonymous' : 'use-credentials'"
|
|
:referrerpolicy="props.privacy ? 'no-referrer' : 'origin'" />
|
|
</figure>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</vTooltip>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { request } from '~/utils/index'
|
|
|
|
const props = defineProps({
|
|
image: {
|
|
type: String,
|
|
required: false,
|
|
},
|
|
title: {
|
|
type: String,
|
|
required: false
|
|
},
|
|
loader: {
|
|
Type: Function,
|
|
required: false,
|
|
},
|
|
privacy: {
|
|
type: Boolean,
|
|
required: false,
|
|
default: true
|
|
}
|
|
});
|
|
|
|
const cache = useSessionCache()
|
|
const toast = useNotification()
|
|
const url = ref()
|
|
const error = ref(false)
|
|
const isPreloading = ref(false)
|
|
|
|
let loadTimer = null;
|
|
const cancelRequest = new AbortController();
|
|
|
|
const defaultLoader = async () => {
|
|
try {
|
|
if (cache.has(props.image)) {
|
|
url.value = cache.get(props.image)
|
|
return
|
|
}
|
|
|
|
const response = await request(props.image, { signal: cancelRequest.signal })
|
|
|
|
if (200 !== response.status) {
|
|
toast.error(`ImageView Request error. ${response.status}: ${response.statusText}`)
|
|
return;
|
|
}
|
|
|
|
const objUrl = URL.createObjectURL(await response.blob());
|
|
|
|
cache.set(props.image, objUrl)
|
|
|
|
url.value = objUrl;
|
|
} catch (e) {
|
|
if ('not_needed' === e) {
|
|
return
|
|
}
|
|
console.error(e)
|
|
toast.error(`ImageView Request failure. ${e}`)
|
|
} finally {
|
|
isPreloading.value = false
|
|
}
|
|
}
|
|
|
|
const stopTimer = async () => {
|
|
if (error.value) {
|
|
return
|
|
}
|
|
|
|
if (url.value) {
|
|
isPreloading.value = false
|
|
url.value = null;
|
|
return;
|
|
}
|
|
|
|
await awaiter(() => isPreloading.value)
|
|
clearTimeout(loadTimer)
|
|
isPreloading.value = false
|
|
url.value = null;
|
|
cancelRequest.abort('not_needed')
|
|
}
|
|
|
|
const loadContent = async () => {
|
|
if (props.loader) {
|
|
return props.loader()
|
|
}
|
|
|
|
return defaultLoader()
|
|
}
|
|
|
|
const clearCache = async () => {
|
|
cache.remove(props.image)
|
|
url.value = '';
|
|
return loadContent()
|
|
}
|
|
|
|
const pImg = e => e.target.naturalHeight > e.target.naturalWidth ? e.target.classList.add('image-portrait') : null
|
|
</script>
|