Files
IdP/apps/frontend/hooks/use-resilient-blob-url.ts
2026-06-29 22:51:25 +03:00

61 lines
1.6 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { createBlobObjectUrl, revokeBlobObjectUrl } from '@/lib/authenticated-media';
import { fetchMediaBlobWithRetry } from '@/lib/resilient-media-fetch';
export type ResilientBlobStatus = 'idle' | 'loading' | 'ready' | 'error';
export function useResilientBlobUrl(
remoteUrl: string | null | undefined,
token: string | null | undefined,
options?: { enabled?: boolean; label?: string; mimeType?: string }
) {
const [blobUrl, setBlobUrl] = useState<string | null>(null);
const [status, setStatus] = useState<ResilientBlobStatus>('idle');
useEffect(() => {
const enabled = options?.enabled !== false;
if (!remoteUrl || !token || !enabled) {
setBlobUrl(null);
setStatus('idle');
return;
}
let cancelled = false;
let objectUrl: string | null = null;
async function load() {
setStatus('loading');
try {
const blob = await fetchMediaBlobWithRetry(remoteUrl!, token!, {
mimeType: options?.mimeType,
label: options?.label
});
if (cancelled) return;
objectUrl = createBlobObjectUrl(blob);
setBlobUrl(objectUrl);
setStatus('ready');
} catch {
if (cancelled) return;
setBlobUrl(null);
setStatus('error');
}
}
void load();
return () => {
cancelled = true;
revokeBlobObjectUrl(objectUrl ?? undefined);
};
}, [options?.enabled, options?.label, options?.mimeType, remoteUrl, token]);
return {
blobUrl,
status,
isLoading: status === 'loading',
hasError: status === 'error'
};
}