Files
chore/web/vue-app/src/common/imageCache.ts
2025-11-20 14:06:59 -05:00

65 lines
1.6 KiB
TypeScript

export const DEFAULT_IMAGE_CACHE = 'images-v1'
const objectUrlMap = new Map<string, string>()
export async function getCachedImageUrl(
imageId: string,
cacheName = DEFAULT_IMAGE_CACHE,
): Promise<string> {
if (!imageId) throw new Error('imageId required')
// reuse existing object URL if created in this session
const existing = objectUrlMap.get(imageId)
if (existing) return existing
const requestUrl = `/api/image/request/${imageId}`
// Try Cache Storage first
let response: Response | undefined
if ('caches' in window) {
const cache = await caches.open(cacheName)
response = await cache.match(requestUrl)
if (!response) {
const fetched = await fetch(requestUrl)
if (!fetched.ok) throw new Error(`HTTP ${fetched.status}`)
// store a clone in Cache Storage (non-blocking)
cache.put(requestUrl, fetched.clone()).catch((e) => {
console.warn('Cache put failed:', e)
})
response = fetched
}
} else {
const fetched = await fetch(requestUrl)
if (!fetched.ok) throw new Error(`HTTP ${fetched.status}`)
response = fetched
}
const blob = await response.blob()
const objectUrl = URL.createObjectURL(blob)
objectUrlMap.set(imageId, objectUrl)
return objectUrl
}
export function revokeImageUrl(imageId: string) {
const url = objectUrlMap.get(imageId)
if (url) {
try {
URL.revokeObjectURL(url)
} catch (e) {
/* ignore */
}
objectUrlMap.delete(imageId)
}
}
export function revokeAllImageUrls() {
for (const url of objectUrlMap.values()) {
try {
URL.revokeObjectURL(url)
} catch (e) {
/* ignore */
}
}
objectUrlMap.clear()
}