import { useState, useEffect, useCallback } from 'react'; import { usePersSDK } from '../providers/PersSDKProvider'; import type { TokenMetadata } from '@explorins/pers-sdk'; /** * React hook for resolving media URLs (IPFS or HTTPS). * * Automatically handles IPFS URLs by resolving them to HTTPS via tenant's gateway. * Non-IPFS URLs are returned as-is. Handles loading and error states. * * Uses sdk.tenants for IPFS resolution (lightweight, no Web3 deps). * * @param url - The URL to resolve (can be ipfs://, https://, or undefined) * @returns Object with resolved URL, loading state, and error * * @example * ```tsx * function NFTImage({ metadataUri }: { metadataUri?: string }) { * const { url, isLoading } = useResolvedUrl(metadataUri); * * if (isLoading) return ; * return ; * } * ``` */ export function useResolvedUrl(url: string | undefined | null) { const { sdk, isInitialized } = usePersSDK(); const [resolvedUrl, setResolvedUrl] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!url || !isInitialized || !sdk) { setResolvedUrl(null); return; } // Non-IPFS URLs don't need resolution if (!url.startsWith('ipfs://')) { setResolvedUrl(url); return; } setIsLoading(true); setError(null); sdk.tenants.resolveIpfsUrl(url) .then((resolved) => { setResolvedUrl(resolved); }) .catch((err) => { setError(err instanceof Error ? err.message : 'Failed to resolve URL'); setResolvedUrl(null); }) .finally(() => { setIsLoading(false); }); }, [url, isInitialized, sdk]); return { /** Resolved HTTPS URL (or original if not IPFS) */ url: resolvedUrl, /** Whether resolution is in progress */ isLoading, /** Error message if resolution failed */ error, }; } /** * React hook for fetching NFT/token metadata from a URI. * * Handles IPFS resolution automatically and returns parsed metadata * with all nested IPFS URLs already resolved to HTTPS. * * Uses sdk.tenants for metadata fetching (lightweight, no Web3 deps). * * @param metadataUri - The metadata URI to fetch (can be ipfs:// or https://) * @returns Object with metadata, loading state, error, and refresh function * * @example * ```tsx * function NFTDetails({ metadataUri }: { metadataUri?: string }) { * const { metadata, isLoading, error } = useMetadata(metadataUri); * * if (isLoading) return ; * if (error) return Error: {error}; * if (!metadata) return null; * * return ( * * * {metadata.name} * {metadata.description} * * ); * } * ``` */ export function useMetadata(metadataUri: string | undefined | null) { const { sdk, isInitialized } = usePersSDK(); const [metadata, setMetadata] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const fetchMetadata = useCallback(async () => { if (!metadataUri || !isInitialized || !sdk) { setMetadata(null); return; } setIsLoading(true); setError(null); try { const result = await sdk.tenants.fetchAndProcessMetadata(metadataUri); setMetadata(result); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to fetch metadata'); setMetadata(null); } finally { setIsLoading(false); } }, [metadataUri, isInitialized, sdk]); useEffect(() => { fetchMetadata(); }, [fetchMetadata]); return { /** Parsed metadata with resolved IPFS URLs (imageUrl, animationUrl, etc.) */ metadata, /** Whether fetch is in progress */ isLoading, /** Error message if fetch failed */ error, /** Manually refresh metadata */ refresh: fetchMetadata, }; } // Re-export with descriptive aliases export { useResolvedUrl as useIPFSUrl }; /** * React hook for loading transaction/token metadata. * * Convenience wrapper around useMetadata specifically for transaction items. * Pass a transaction's metadataUri to get the NFT image, name, description, etc. * * @param transaction - Transaction object with optional metadataUri field * @returns Object with metadata, loading state, and image URL * * @example * ```tsx * import { useTransactionMetadata } from '@explorins/pers-sdk-react-native'; * * function TransactionItem({ tx }: { tx: TransactionDTO }) { * const { metadata, isLoading, imageUrl } = useTransactionMetadata(tx); * * if (isLoading) return ; * * return ( * * {imageUrl && } * {metadata?.name || tx.type} * {tx.amount} tokens * * ); * } * ``` */ export function useTransactionMetadata(transaction: { metadataUri?: string | null } | null | undefined) { const { metadata, isLoading, error, refresh } = useMetadata(transaction?.metadataUri); return { /** Parsed metadata with resolved URLs */ metadata, /** Whether metadata is loading */ isLoading, /** Error message if fetch failed */ error, /** Convenience: resolved image URL (or undefined) */ imageUrl: metadata?.imageUrl, /** Convenience: token/NFT name (or undefined) */ name: metadata?.name, /** Convenience: token/NFT description (or undefined) */ description: metadata?.description, /** Manually refresh metadata */ refresh, }; }