"use client"; import { useEffect, useState } from "react"; import { readPendingCommunityAvatarFile } from "../../../../lib/create/pendingCommunityAvatarUpload"; function trimmedAvatarUrl(url: string | null): string | null { if (typeof url !== "string") return null; const trimmed = url.trim(); return trimmed.length > 0 ? trimmed : null; } /** * Logo src for review cards. Uses the uploaded URL when the draft has one, * otherwise the guest file staged in IndexedDB. * * @param serverUrl `communityAvatarUrl` from create-flow state, if any. * @returns A URL the Rule card can render, or `null` when there is no logo. * * @example * const logoUrl = useCommunityAvatarDisplayUrl(state.communityAvatarUrl ?? null); */ export function useCommunityAvatarDisplayUrl( serverUrl: string | null | undefined, ): string | null { const resolvedServerUrl = trimmedAvatarUrl(serverUrl ?? null); const [objectUrl, setObjectUrl] = useState(null); useEffect(() => { if (resolvedServerUrl) { setObjectUrl((prev) => { if (prev) URL.revokeObjectURL(prev); return null; }); return; } let cancelled = false; let created: string | null = null; void (async () => { try { const file = await readPendingCommunityAvatarFile(); if (cancelled || !file) return; created = URL.createObjectURL(file); if (cancelled) { URL.revokeObjectURL(created); created = null; return; } setObjectUrl((prev) => { if (prev && prev !== created) URL.revokeObjectURL(prev); return created; }); } catch { // IndexedDB missing or blocked: the card keeps the placeholder. } })(); return () => { cancelled = true; if (created) URL.revokeObjectURL(created); }; }, [resolvedServerUrl]); return resolvedServerUrl ?? objectUrl; }