atmosphere explorer

cleanup img blob loading

handle.invalid d4652f59 239337fb

verified
+16 -22
+16 -22
src/components/json.tsx
··· 3 import { 4 createContext, 5 createEffect, 6 createSignal, 7 ErrorBoundary, 8 For, 9 on, 10 - onCleanup, 11 - onMount, 12 Show, 13 useContext, 14 } from "solid-js"; ··· 263 (blob.mimeType.startsWith("image/") || blob.mimeType === "video/mp4"); 264 265 const MediaDisplay = () => { 266 - const [imageObjectUrl, setImageObjectUrl] = createSignal<string>(); 267 268 - onMount(() => { 269 - if (blob.mimeType.startsWith("image/")) { 270 - const fetchImage = async () => { 271 - const res = await fetch( 272 - `https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${ctx.repo}&cid=${blob.ref.$link}`, 273 - ); 274 - if (!res.ok) throw new Error(res.statusText); 275 - const blobData = await res.blob(); 276 - const url = URL.createObjectURL(blobData); 277 - setImageObjectUrl(url); 278 - }; 279 - fetchImage().catch((err) => console.error("Failed to load image:", err)); 280 - } 281 - }); 282 283 - onCleanup(() => { 284 - if (imageObjectUrl()) URL.revokeObjectURL(imageObjectUrl()!); 285 - }); 286 287 return ( 288 <div> ··· 290 <Show when={!hide()}> 291 <Show when={blob.mimeType.startsWith("image/")}> 292 <Show 293 - when={imageObjectUrl()} 294 fallback={ 295 <div class="flex h-48 w-48 items-center justify-center rounded bg-neutral-200 dark:bg-neutral-800"> 296 <span class="iconify lucide--loader-circle animate-spin text-xl text-neutral-400 dark:text-neutral-500"></span> ··· 299 > 300 <img 301 class="h-auto max-h-48 max-w-64 object-contain" 302 - src={imageObjectUrl()} 303 onLoad={() => setMediaLoaded(true)} 304 /> 305 </Show>
··· 3 import { 4 createContext, 5 createEffect, 6 + createResource, 7 createSignal, 8 ErrorBoundary, 9 For, 10 on, 11 Show, 12 useContext, 13 } from "solid-js"; ··· 262 (blob.mimeType.startsWith("image/") || blob.mimeType === "video/mp4"); 263 264 const MediaDisplay = () => { 265 + const [imageUrl] = createResource( 266 + () => (blob.mimeType.startsWith("image/") ? blob.ref.$link : null), 267 + async (cid) => { 268 + const url = `https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${ctx.repo}&cid=${cid}`; 269 270 + await new Promise<void>((resolve) => { 271 + const img = new Image(); 272 + img.src = url; 273 + img.onload = () => resolve(); 274 + img.onerror = () => resolve(); 275 + }); 276 277 + return url; 278 + }, 279 + ); 280 281 return ( 282 <div> ··· 284 <Show when={!hide()}> 285 <Show when={blob.mimeType.startsWith("image/")}> 286 <Show 287 + when={!imageUrl.loading && imageUrl()} 288 fallback={ 289 <div class="flex h-48 w-48 items-center justify-center rounded bg-neutral-200 dark:bg-neutral-800"> 290 <span class="iconify lucide--loader-circle animate-spin text-xl text-neutral-400 dark:text-neutral-500"></span> ··· 293 > 294 <img 295 class="h-auto max-h-48 max-w-64 object-contain" 296 + src={imageUrl()} 297 onLoad={() => setMediaLoaded(true)} 298 /> 299 </Show>