| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- import { fileApi } from "./fileApi";
- import { parseContentDispositionFilename } from "./filename";
- interface PreviewRequest {
- controller: AbortController;
- promise: Promise<Awaited<ReturnType<typeof fileApi.preview>>>;
- consumers: number;
- }
- const previewRequests = new Map<string, PreviewRequest>();
- const previewResults = new Map<string, Awaited<ReturnType<typeof fileApi.preview>>>();
- 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<Awaited<ReturnType<typeof fileApi.preview>>>;
- 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);
- }
- }
|