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,
};
}