import { SymbolView } from "./AppSymbol"; import { Image } from "expo-image"; import { useLayoutEffect, useMemo, useState } from "react"; import { View } from "react-native"; import type { EnvironmentId } from "@t3tools/contracts"; import { getProjectFaviconCacheKey, isProjectFaviconFallbackUrl, } from "@t3tools/shared/projectFavicon"; import { useAssetUrl } from "../state/assets"; import { beginProjectFaviconRequest, createProjectFaviconRequest, hasLoadedProjectFavicon, markProjectFaviconFailed, markProjectFaviconLoaded, } from "./projectFaviconCache"; /* ─── Component ──────────────────────────────────────────────────────── */ export function ProjectFavicon(props: { readonly environmentId: EnvironmentId; readonly open?: boolean; readonly size?: number; readonly projectTitle: string; readonly workspaceRoot?: string | null; readonly faviconPath?: string | null; }) { const size = props.size ?? 42; const faviconUrl = useAssetUrl( props.environmentId, props.workspaceRoot === null || props.workspaceRoot === undefined ? null : { _tag: "project-favicon", cwd: props.workspaceRoot, ...(props.faviconPath ? { path: props.faviconPath } : {}), }, ); const renderableFaviconUrl = isProjectFaviconFallbackUrl(faviconUrl) ? null : faviconUrl; const cacheKey = renderableFaviconUrl && props.workspaceRoot ? getProjectFaviconCacheKey(props.environmentId, props.workspaceRoot, renderableFaviconUrl) : null; return ( ); } function ProjectFaviconImage(props: { readonly cacheKey: string | null; readonly faviconUrl: string | null; readonly open?: boolean; readonly projectTitle: string; readonly size: number; }) { const faviconRequest = useMemo( () => createProjectFaviconRequest(props.cacheKey, props.faviconUrl), [props.cacheKey, props.faviconUrl], ); const [activeFaviconRequest, setActiveFaviconRequest] = useState(null); useLayoutEffect(() => { if (faviconRequest === null) return; const endRequest = beginProjectFaviconRequest(faviconRequest); setActiveFaviconRequest(faviconRequest); return endRequest; }, [faviconRequest]); const [status, setStatus] = useState<"loading" | "loaded" | "error">(() => hasLoadedProjectFavicon(props.cacheKey) ? "loaded" : "loading", ); const requestIsActive = faviconRequest !== null && activeFaviconRequest === faviconRequest; const showImage = requestIsActive && status === "loaded"; return ( {/* Folder icon fallback (matches web's FolderIcon) */} {!showImage ? ( ) : null} {/* Favicon image (hidden until loaded) */} {requestIsActive ? ( { if (!markProjectFaviconLoaded(faviconRequest)) return; setStatus("loaded"); }} onError={() => { if (!markProjectFaviconFailed(faviconRequest)) return; setStatus("error"); }} /> ) : null} ); }