import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; import { Atom } from "effect/unstable/reactivity"; const WORKSPACE_IMAGE_IDLE_TTL_MS = 30 * 60_000; type ImagePrefetch = (uri: string) => Promise; class WorkspaceImageCacheKey extends Data.Class<{ readonly uri: string }> {} export class WorkspaceImagePrefetchError extends Data.TaggedError("WorkspaceImagePrefetchError")<{ readonly cause?: unknown; readonly uri: string; }> {} async function prefetchWithNativeImage(uri: string): Promise { const { Image } = await import("react-native"); return Image.prefetch(uri); } export function createWorkspaceFileImageAtomFamily(options?: { readonly idleTtlMs?: number; readonly prefetch?: ImagePrefetch; }) { const idleTtlMs = options?.idleTtlMs ?? WORKSPACE_IMAGE_IDLE_TTL_MS; const prefetch = options?.prefetch ?? prefetchWithNativeImage; const family = Atom.family((key: WorkspaceImageCacheKey) => Atom.make( Effect.tryPromise({ try: async () => { const cached = await prefetch(key.uri); if (!cached) { throw new WorkspaceImagePrefetchError({ uri: key.uri }); } return key.uri; }, catch: (cause) => cause instanceof WorkspaceImagePrefetchError ? cause : new WorkspaceImagePrefetchError({ uri: key.uri, cause }), }), ).pipe(Atom.setIdleTTL(idleTtlMs), Atom.withLabel(`mobile:workspace-image:${key.uri}`)), ); return (uri: string) => family(new WorkspaceImageCacheKey({ uri })); } export const workspaceFileImageAtom = createWorkspaceFileImageAtomFamily();