import type { EnvironmentId } from "@t3tools/contracts"; import { getProjectFaviconCacheKey, isProjectFaviconFallbackUrl, } from "@t3tools/shared/projectFavicon"; import { FolderIcon } from "lucide-react"; import type { ComponentType } from "react"; import { useState } from "react"; import { useAssetUrlState } from "../assets/assetUrls"; import { cn } from "~/lib/utils"; const loadedProjectFaviconSrcs = new Map(); export function ProjectFavicon(input: { environmentId: EnvironmentId; cwd: string; faviconPath?: string | null | undefined; className?: string | undefined; fallbackIcon?: ComponentType<{ className?: string }>; }) { const state = useProjectFaviconAsset(input); const src = state._tag === "Success" ? state.url : null; const FallbackIcon = input.fallbackIcon ?? FolderIcon; if (!src || isProjectFaviconFallbackUrl(src)) { return ; } const cacheKey = getProjectFaviconCacheKey(input.environmentId, input.cwd, src); return ( ); } export function useProjectFaviconAsset(input: { readonly environmentId: EnvironmentId; readonly cwd: string; readonly faviconPath?: string | null | undefined; }) { return useAssetUrlState(input.environmentId, { _tag: "project-favicon", cwd: input.cwd, ...(input.faviconPath ? { path: input.faviconPath } : {}), }); } function ProjectFaviconFallback({ className, icon: Icon, }: { readonly className?: string | undefined; readonly icon: ComponentType<{ className?: string }>; }) { return ; } function ProjectFaviconImage({ cacheKey, src, className, fallbackIcon: FallbackIcon, }: { readonly cacheKey: string; readonly src: string; readonly className?: string | undefined; readonly fallbackIcon: ComponentType<{ className?: string }>; }) { const [displayedSrc, setDisplayedSrc] = useState( () => loadedProjectFaviconSrcs.get(cacheKey) ?? null, ); const isLoading = displayedSrc !== src; const handleLoadError = (failedSrc: string) => { if (loadedProjectFaviconSrcs.get(cacheKey) === failedSrc) { loadedProjectFaviconSrcs.delete(cacheKey); } setDisplayedSrc((currentSrc) => (currentSrc === failedSrc ? null : currentSrc)); }; return ( <> {displayedSrc === null ? ( ) : null} {displayedSrc ? ( handleLoadError(displayedSrc)} /> ) : null} {isLoading ? ( { loadedProjectFaviconSrcs.set(cacheKey, src); setDisplayedSrc(src); }} onError={() => handleLoadError(src)} /> ) : null} ); }