// Fetches the arcadia profile on app boot (and after login) and caches // the resolved avatar URL in localStorage so the appbar's shows // immediately, without waiting for the user to navigate to /profile. import { useEffect } from "react" import { useArcadiaClient } from "@crema/arcadia-client" import { fetchDigitalObjectAsBlobUrl } from "~/lib/arcadia/digital-objects" import { getProfile, pickAvatarUrl } from "~/lib/arcadia/profiles" import { loadProfile, saveProfile } from "~/lib/profile" export function ProfileBootstrap() { const arcadia = useArcadiaClient() useEffect(() => { let cancelled = false const tryBootstrap = async () => { const token = typeof window !== "undefined" ? sessionStorage.getItem("arcadia_access_token") : null if (!token) return try { const p = await getProfile(arcadia) if (cancelled) return const persistentUrl = pickAvatarUrl(p) const current = loadProfile() const cachedIsStaleBlob = current.avatarUrl?.startsWith("blob:") ?? false if (persistentUrl) { if (current.avatarUrl !== persistentUrl) { saveProfile({ ...current, avatarUrl: persistentUrl }) } return } // No persistent variant yet but the user has an avatar — fetch // the raw bytes as a blob URL. This also covers the "stale blob // URL from previous session" case: replace it with a fresh one. if (p.avatar_digital_object_id) { if (cachedIsStaleBlob) { // Clear the stale URL immediately so the appbar drops back // to initials while we refetch (better than a broken image). saveProfile({ ...current, avatarUrl: "" }) } try { const baseUrl = (import.meta.env.VITE_ARCADIA_URL as string | undefined) ?? "http://localhost:4000" const tenantId = (import.meta.env.VITE_ARCADIA_TENANT as string | undefined) ?? "default" const blobUrl = await fetchDigitalObjectAsBlobUrl( baseUrl, p.avatar_digital_object_id, token, tenantId, ) if (cancelled) return const fresh = loadProfile() saveProfile({ ...fresh, avatarUrl: blobUrl }) } catch { // Best-effort; appbar will show initials until processing completes. } return } // No avatar at all — clear any stale URL the cache might still hold. if (current.avatarUrl) { saveProfile({ ...current, avatarUrl: "" }) } } catch { // 401 / network — silently skip; will retry on next session change. } } void tryBootstrap() const onSessionChange = () => void tryBootstrap() window.addEventListener("crema:session-change", onSessionChange) return () => { cancelled = true window.removeEventListener("crema:session-change", onSessionChange) } }, [arcadia]) return null }