import { fileApi } from "./fileApi"; import { parseContentDispositionFilename } from "./filename"; interface PreviewRequest { controller: AbortController; promise: Promise>>; consumers: number; } const previewRequests = new Map(); const previewResults = new Map>>(); function previewKey(id: string, version: string | number = ""): string { return `${id}:${version}`; } export function loadPreviewOnce(id: string, version: string | number = "") { const key = previewKey(id, version); const cached = previewResults.get(key); if (cached) return Promise.resolve(cached); const existing = previewRequests.get(key); if (existing) return existing.promise; const controller = new AbortController(); const request: PreviewRequest = { controller, consumers: 0, promise: Promise.resolve(undefined as never), }; request.promise = fileApi.preview(id, controller.signal) .then((result) => { previewResults.set(key, result); return result; }) .finally(() => { if (previewRequests.get(key) === request) previewRequests.delete(key); }); previewRequests.set(key, request); return request.promise; } export function acquirePreview(id: string, version: string | number = ""): { promise: Promise>>; release: () => void; } { const key = previewKey(id, version); const promise = loadPreviewOnce(id, version); const request = previewRequests.get(key); if (!request) return { promise, release: () => undefined }; request.consumers += 1; let released = false; return { promise, release: () => { if (released) return; released = true; request.consumers -= 1; globalThis.setTimeout(() => { if ( request.consumers === 0 && previewRequests.get(key) === request ) { request.controller.abort(); previewRequests.delete(key); } }, 0); }, }; } export function createPreviewUrl(blob: Blob): string { return URL.createObjectURL(blob); } export function revokeObjectUrl(url: string | null): void { if (url) URL.revokeObjectURL(url); } export async function downloadFile( id: string, fallbackName: string, signal?: AbortSignal, ): Promise<{ filename: string; requestId: string }> { const result = await fileApi.download(id, signal); const filename = parseContentDispositionFilename( result.contentDisposition, fallbackName, ); const url = URL.createObjectURL(result.blob); const anchor = document.createElement("a"); try { anchor.href = url; anchor.download = filename; anchor.style.display = "none"; document.body.appendChild(anchor); anchor.click(); } finally { anchor.remove(); URL.revokeObjectURL(url); } return { filename, requestId: result.requestId }; } export function clearPreviewRequestsForTests(): void { previewRequests.clear(); previewResults.clear(); } export function invalidatePreview(id: string): void { for (const key of previewResults.keys()) { if (key.startsWith(`${id}:`)) previewResults.delete(key); } }