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 { useThemeColor } from "../lib/useThemeColor";
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;
}) {
const size = props.size ?? 42;
const faviconUrl = useAssetUrl(
props.environmentId,
props.workspaceRoot === null || props.workspaceRoot === undefined
? null
: { _tag: "project-favicon", cwd: props.workspaceRoot },
);
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 iconMuted = useThemeColor("--color-icon-subtle");
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}
);
}