import { useState } from 'react'; import { Download, ImageOff } from 'lucide-react'; import { authFetch } from '../../lib/auth'; import { useAuthedFileUrl } from '../../lib/authedFile'; interface Props { src: string; alt?: string; } function getFilename(src: string): string { const parts = src.split('/'); return parts[parts.length - 1] || 'bloby-image'; } export default function BlobyImageCard({ src, alt }: Props) { const [failed, setFailed] = useState(false); // `src` may be a same-origin /api/files/* path (needs the auth token, which a native // can't send) or an external URL — useAuthedFileUrl only fetches+authes the // former and passes external URLs through untouched (so the token never leaves origin). const { url: resolvedSrc, status } = useAuthedFileUrl(src); const handleDownload = async () => { try { const res = src.startsWith('/api/files/') ? await authFetch(src) : await fetch(src); const blob = await res.blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = getFilename(src); document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch { window.open(src, '_blank'); } }; if (failed || status === 'error') { return (
{alt || 'Image not found'}
); } return (
{resolvedSrc ? ( {alt setFailed(true)} /> ) : (
)}
{alt && (
{alt}
)}
); }