56 lines
1.3 KiB
Svelte
56 lines
1.3 KiB
Svelte
<script lang="ts">
|
|
import {apiGetPhotoBlob} from '../api/index'
|
|
|
|
let { photoId, version = 'MEDIUM', alt = 'Photo', class: cls = '' }: {
|
|
photoId: string
|
|
version?: 'ORIGINAL' | 'LARGE' | 'MEDIUM' | 'SMALL'
|
|
alt?: string
|
|
class?: string
|
|
} = $props()
|
|
|
|
let src = $state<string | null>(null)
|
|
let error = $state(false)
|
|
|
|
$effect(() => {
|
|
let objectUrl: string | null = null
|
|
let cancelled = false
|
|
error = false
|
|
src = null
|
|
|
|
async function load(attempt = 0) {
|
|
try {
|
|
const blob = await apiGetPhotoBlob(photoId, version)
|
|
if (cancelled) return
|
|
if (objectUrl) URL.revokeObjectURL(objectUrl)
|
|
objectUrl = URL.createObjectURL(blob)
|
|
src = objectUrl
|
|
} catch {
|
|
if (cancelled) return
|
|
if (attempt < 3) {
|
|
await new Promise(r => setTimeout(r, 1500 * (attempt + 1)))
|
|
if (!cancelled) load(attempt + 1)
|
|
} else {
|
|
error = true
|
|
}
|
|
}
|
|
}
|
|
|
|
load()
|
|
|
|
return () => {
|
|
cancelled = true
|
|
if (objectUrl) URL.revokeObjectURL(objectUrl)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
{#if error}
|
|
<div class="bg-base-300 flex items-center justify-center {cls}">
|
|
<span class="text-base-content/40 text-sm">No image</span>
|
|
</div>
|
|
{:else if src}
|
|
<img {src} {alt} class={cls} />
|
|
{:else}
|
|
<div class="bg-base-300 animate-pulse {cls}"></div>
|
|
{/if}
|